mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: max context tokens
This commit is contained in:
Executable
BIN
Binary file not shown.
@@ -117,6 +117,7 @@ CREATE TABLE IF NOT EXISTS bots (
|
|||||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||||
status TEXT NOT NULL DEFAULT 'ready',
|
status TEXT NOT NULL DEFAULT 'ready',
|
||||||
max_context_load_time INTEGER NOT NULL DEFAULT 1440,
|
max_context_load_time INTEGER NOT NULL DEFAULT 1440,
|
||||||
|
max_context_tokens INTEGER NOT NULL DEFAULT 0,
|
||||||
language TEXT NOT NULL DEFAULT 'auto',
|
language TEXT NOT NULL DEFAULT 'auto',
|
||||||
allow_guest BOOLEAN NOT NULL DEFAULT false,
|
allow_guest BOOLEAN NOT NULL DEFAULT false,
|
||||||
chat_model_id UUID REFERENCES models(id) ON DELETE SET NULL,
|
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')),
|
role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system', 'tool')),
|
||||||
content JSONB NOT NULL,
|
content JSONB NOT NULL,
|
||||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||||
|
usage JSONB,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
@@ -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;
|
||||||
+6
-6
@@ -1,21 +1,21 @@
|
|||||||
-- name: CreateBot :one
|
-- name: CreateBot :one
|
||||||
INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status)
|
INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
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
|
-- 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
|
FROM bots
|
||||||
WHERE id = $1;
|
WHERE id = $1;
|
||||||
|
|
||||||
-- name: ListBotsByOwner :many
|
-- 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
|
FROM bots
|
||||||
WHERE owner_user_id = $1
|
WHERE owner_user_id = $1
|
||||||
ORDER BY created_at DESC;
|
ORDER BY created_at DESC;
|
||||||
|
|
||||||
-- name: ListBotsByMember :many
|
-- 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
|
FROM bots b
|
||||||
JOIN bot_members m ON m.bot_id = b.id
|
JOIN bot_members m ON m.bot_id = b.id
|
||||||
WHERE m.user_id = $1
|
WHERE m.user_id = $1
|
||||||
@@ -29,14 +29,14 @@ SET display_name = $2,
|
|||||||
metadata = $5,
|
metadata = $5,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE id = $1
|
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
|
-- name: UpdateBotOwner :one
|
||||||
UPDATE bots
|
UPDATE bots
|
||||||
SET owner_user_id = $2,
|
SET owner_user_id = $2,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE id = $1
|
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
|
-- name: UpdateBotStatus :exec
|
||||||
UPDATE bots
|
UPDATE bots
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ INSERT INTO bot_history_messages (
|
|||||||
source_reply_to_message_id,
|
source_reply_to_message_id,
|
||||||
role,
|
role,
|
||||||
content,
|
content,
|
||||||
metadata
|
metadata,
|
||||||
|
usage
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
sqlc.arg(bot_id),
|
sqlc.arg(bot_id),
|
||||||
@@ -21,7 +22,8 @@ VALUES (
|
|||||||
sqlc.narg(source_reply_to_message_id)::text,
|
sqlc.narg(source_reply_to_message_id)::text,
|
||||||
sqlc.arg(role),
|
sqlc.arg(role),
|
||||||
sqlc.arg(content),
|
sqlc.arg(content),
|
||||||
sqlc.arg(metadata)
|
sqlc.arg(metadata),
|
||||||
|
sqlc.arg(usage)
|
||||||
)
|
)
|
||||||
RETURNING
|
RETURNING
|
||||||
id,
|
id,
|
||||||
@@ -35,6 +37,7 @@ RETURNING
|
|||||||
role,
|
role,
|
||||||
content,
|
content,
|
||||||
metadata,
|
metadata,
|
||||||
|
usage,
|
||||||
created_at;
|
created_at;
|
||||||
|
|
||||||
-- name: ListMessages :many
|
-- name: ListMessages :many
|
||||||
@@ -50,6 +53,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
@@ -72,6 +76,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
@@ -94,6 +99,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
@@ -117,6 +123,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
SELECT
|
SELECT
|
||||||
bots.id AS bot_id,
|
bots.id AS bot_id,
|
||||||
bots.max_context_load_time,
|
bots.max_context_load_time,
|
||||||
|
bots.max_context_tokens,
|
||||||
bots.language,
|
bots.language,
|
||||||
bots.allow_guest,
|
bots.allow_guest,
|
||||||
chat_models.model_id AS chat_model_id,
|
chat_models.model_id AS chat_model_id,
|
||||||
@@ -19,6 +20,7 @@ WHERE bots.id = $1;
|
|||||||
WITH updated AS (
|
WITH updated AS (
|
||||||
UPDATE bots
|
UPDATE bots
|
||||||
SET max_context_load_time = sqlc.arg(max_context_load_time),
|
SET max_context_load_time = sqlc.arg(max_context_load_time),
|
||||||
|
max_context_tokens = sqlc.arg(max_context_tokens),
|
||||||
language = sqlc.arg(language),
|
language = sqlc.arg(language),
|
||||||
allow_guest = sqlc.arg(allow_guest),
|
allow_guest = sqlc.arg(allow_guest),
|
||||||
chat_model_id = COALESCE(sqlc.narg(chat_model_id)::uuid, bots.chat_model_id),
|
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),
|
search_provider_id = COALESCE(sqlc.narg(search_provider_id)::uuid, bots.search_provider_id),
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE bots.id = sqlc.arg(id)
|
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
|
SELECT
|
||||||
updated.id AS bot_id,
|
updated.id AS bot_id,
|
||||||
updated.max_context_load_time,
|
updated.max_context_load_time,
|
||||||
|
updated.max_context_tokens,
|
||||||
updated.language,
|
updated.language,
|
||||||
updated.allow_guest,
|
updated.allow_guest,
|
||||||
chat_models.model_id AS chat_model_id,
|
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
|
-- name: DeleteSettingsByBotID :exec
|
||||||
UPDATE bots
|
UPDATE bots
|
||||||
SET max_context_load_time = 1440,
|
SET max_context_load_time = 1440,
|
||||||
|
max_context_tokens = 0,
|
||||||
language = 'auto',
|
language = 'auto',
|
||||||
allow_guest = false,
|
allow_guest = false,
|
||||||
chat_model_id = NULL,
|
chat_model_id = NULL,
|
||||||
|
|||||||
@@ -150,6 +150,12 @@ type gatewayRequest struct {
|
|||||||
type gatewayResponse struct {
|
type gatewayResponse struct {
|
||||||
Messages []conversation.ModelMessage `json:"messages"`
|
Messages []conversation.ModelMessage `json:"messages"`
|
||||||
Skills []string `json:"skills"`
|
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.
|
// 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
|
return resolvedContext{}, err
|
||||||
}
|
}
|
||||||
maxCtx := coalescePositiveInt(req.MaxContextLoadTime, botSettings.MaxContextLoadTime, defaultMaxContextMinutes)
|
maxCtx := coalescePositiveInt(req.MaxContextLoadTime, botSettings.MaxContextLoadTime, defaultMaxContextMinutes)
|
||||||
|
maxTokens := botSettings.MaxContextTokens
|
||||||
|
|
||||||
var messages []conversation.ModelMessage
|
var messages []conversation.ModelMessage
|
||||||
if !skipHistory && r.conversationSvc != nil {
|
if !skipHistory && r.conversationSvc != nil {
|
||||||
messages, err = r.loadMessages(ctx, req.ChatID, maxCtx)
|
loaded, loadErr := r.loadMessages(ctx, req.ChatID, maxCtx)
|
||||||
if err != nil {
|
if loadErr != nil {
|
||||||
return resolvedContext{}, err
|
return resolvedContext{}, loadErr
|
||||||
}
|
}
|
||||||
|
messages = trimMessagesByTokens(loaded, maxTokens)
|
||||||
}
|
}
|
||||||
if memoryMsg := r.loadMemoryContextMessage(ctx, req); memoryMsg != nil {
|
if memoryMsg := r.loadMemoryContextMessage(ctx, req); memoryMsg != nil {
|
||||||
messages = append(messages, *memoryMsg)
|
messages = append(messages, *memoryMsg)
|
||||||
@@ -291,7 +299,7 @@ func (r *Resolver) Chat(ctx context.Context, req conversation.ChatRequest) (conv
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return conversation.ChatResponse{}, err
|
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{}, err
|
||||||
}
|
}
|
||||||
return conversation.ChatResponse{
|
return conversation.ChatResponse{
|
||||||
@@ -345,7 +353,7 @@ func (r *Resolver) TriggerSchedule(ctx context.Context, botID string, payload sc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return r.storeRound(ctx, req, resp.Messages)
|
return r.storeRound(ctx, req, resp.Messages, resp.Usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- StreamChat ---
|
// --- StreamChat ---
|
||||||
@@ -552,7 +560,7 @@ func (r *Resolver) tryStoreStream(ctx context.Context, req conversation.ChatRequ
|
|||||||
if eventType == "done" {
|
if eventType == "done" {
|
||||||
var resp gatewayResponse
|
var resp gatewayResponse
|
||||||
if err := json.Unmarshal([]byte(data), &resp); err == nil && len(resp.Messages) > 0 {
|
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"`
|
Data json.RawMessage `json:"data"`
|
||||||
Messages []conversation.ModelMessage `json:"messages"`
|
Messages []conversation.ModelMessage `json:"messages"`
|
||||||
Skills []string `json:"skills"`
|
Skills []string `json:"skills"`
|
||||||
|
Usage json.RawMessage `json:"usage,omitempty"`
|
||||||
}
|
}
|
||||||
if err := json.Unmarshal([]byte(data), &envelope); err == nil {
|
if err := json.Unmarshal([]byte(data), &envelope); err == nil {
|
||||||
if (envelope.Type == "agent_end" || envelope.Type == "done") && len(envelope.Messages) > 0 {
|
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 {
|
if envelope.Type == "done" && len(envelope.Data) > 0 {
|
||||||
var resp gatewayResponse
|
var resp gatewayResponse
|
||||||
if err := json.Unmarshal(envelope.Data, &resp); err == nil && len(resp.Messages) > 0 {
|
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: [...]}
|
// fallback: data: {messages: [...]}
|
||||||
var resp gatewayResponse
|
var resp gatewayResponse
|
||||||
if err := json.Unmarshal([]byte(data), &resp); err == nil && len(resp.Messages) > 0 {
|
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
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -648,7 +657,12 @@ func (r *Resolver) resolveContainerID(ctx context.Context, botID, explicit strin
|
|||||||
|
|
||||||
// --- message loading ---
|
// --- 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 {
|
if r.messageService == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -657,7 +671,7 @@ func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var result []conversation.ModelMessage
|
var result []messageWithUsage
|
||||||
for _, m := range msgs {
|
for _, m := range msgs {
|
||||||
var mm conversation.ModelMessage
|
var mm conversation.ModelMessage
|
||||||
if err := json.Unmarshal(m.Content, &mm); err != nil {
|
if err := json.Unmarshal(m.Content, &mm); err != nil {
|
||||||
@@ -667,11 +681,84 @@ func (r *Resolver) loadMessages(ctx context.Context, chatID string, maxContextMi
|
|||||||
} else {
|
} else {
|
||||||
mm.Role = m.Role
|
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
|
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 {
|
type memoryContextItem struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
Item memory.MemoryItem
|
Item memory.MemoryItem
|
||||||
@@ -792,9 +879,7 @@ func (r *Resolver) persistUserMessage(ctx context.Context, req conversation.Chat
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage) error {
|
func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest, messages []conversation.ModelMessage, usage json.RawMessage) 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.
|
|
||||||
fullRound := make([]conversation.ModelMessage, 0, len(messages)+1)
|
fullRound := make([]conversation.ModelMessage, 0, len(messages)+1)
|
||||||
hasUserQuery := false
|
hasUserQuery := false
|
||||||
for _, m := range messages {
|
for _, m := range messages {
|
||||||
@@ -813,7 +898,6 @@ func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest,
|
|||||||
}
|
}
|
||||||
for _, m := range messages {
|
for _, m := range messages {
|
||||||
if req.UserMessagePersisted && m.Role == "user" && strings.TrimSpace(m.TextContent()) == strings.TrimSpace(req.Query) {
|
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
|
continue
|
||||||
}
|
}
|
||||||
fullRound = append(fullRound, m)
|
fullRound = append(fullRound, m)
|
||||||
@@ -822,14 +906,12 @@ func (r *Resolver) storeRound(ctx context.Context, req conversation.ChatRequest,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
r.storeMessages(ctx, req, fullRound)
|
r.storeMessages(ctx, req, fullRound, usage)
|
||||||
// Run memory extraction in the background so that the SSE stream can
|
|
||||||
// finish immediately after messages are persisted.
|
|
||||||
go r.storeMemory(context.WithoutCancel(ctx), req.BotID, fullRound)
|
go r.storeMemory(context.WithoutCancel(ctx), req.BotID, fullRound)
|
||||||
return nil
|
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 {
|
if r.messageService == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -838,7 +920,7 @@ func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatReque
|
|||||||
}
|
}
|
||||||
meta := buildRouteMetadata(req)
|
meta := buildRouteMetadata(req)
|
||||||
senderChannelIdentityID, senderUserID := r.resolvePersistSenderIDs(ctx, req)
|
senderChannelIdentityID, senderUserID := r.resolvePersistSenderIDs(ctx, req)
|
||||||
for _, msg := range messages {
|
for i, msg := range messages {
|
||||||
content, err := json.Marshal(msg)
|
content, err := json.Marshal(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Warn("storeMessages: marshal failed", slog.Any("error", err))
|
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)
|
assets = chatAttachmentsToAssetRefs(req.Attachments)
|
||||||
}
|
}
|
||||||
} else if strings.TrimSpace(req.ExternalMessageID) != "" {
|
} 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
|
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{
|
if _, err := r.messageService.Persist(ctx, messagepkg.PersistInput{
|
||||||
BotID: req.BotID,
|
BotID: req.BotID,
|
||||||
RouteID: req.RouteID,
|
RouteID: req.RouteID,
|
||||||
@@ -871,6 +956,7 @@ func (r *Resolver) storeMessages(ctx context.Context, req conversation.ChatReque
|
|||||||
Role: msg.Role,
|
Role: msg.Role,
|
||||||
Content: content,
|
Content: content,
|
||||||
Metadata: meta,
|
Metadata: meta,
|
||||||
|
Usage: msgUsage,
|
||||||
Assets: assets,
|
Assets: assets,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
r.logger.Warn("persist message failed", slog.Any("error", err))
|
r.logger.Warn("persist message failed", slog.Any("error", err))
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
const createBot = `-- name: CreateBot :one
|
const createBot = `-- name: CreateBot :one
|
||||||
INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status)
|
INSERT INTO bots (owner_user_id, type, display_name, avatar_url, is_active, metadata, status)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
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 {
|
type CreateBotParams struct {
|
||||||
@@ -47,6 +47,7 @@ func (q *Queries) CreateBot(ctx context.Context, arg CreateBotParams) (Bot, erro
|
|||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
@@ -84,7 +85,7 @@ func (q *Queries) DeleteBotMember(ctx context.Context, arg DeleteBotMemberParams
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getBotByID = `-- name: GetBotByID :one
|
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
|
FROM bots
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
@@ -101,6 +102,7 @@ func (q *Queries) GetBotByID(ctx context.Context, id pgtype.UUID) (Bot, error) {
|
|||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
@@ -171,7 +173,7 @@ func (q *Queries) ListBotMembers(ctx context.Context, botID pgtype.UUID) ([]BotM
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listBotsByMember = `-- name: ListBotsByMember :many
|
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
|
FROM bots b
|
||||||
JOIN bot_members m ON m.bot_id = b.id
|
JOIN bot_members m ON m.bot_id = b.id
|
||||||
WHERE m.user_id = $1
|
WHERE m.user_id = $1
|
||||||
@@ -196,6 +198,7 @@ func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]B
|
|||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
@@ -217,7 +220,7 @@ func (q *Queries) ListBotsByMember(ctx context.Context, userID pgtype.UUID) ([]B
|
|||||||
}
|
}
|
||||||
|
|
||||||
const listBotsByOwner = `-- name: ListBotsByOwner :many
|
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
|
FROM bots
|
||||||
WHERE owner_user_id = $1
|
WHERE owner_user_id = $1
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
@@ -241,6 +244,7 @@ func (q *Queries) ListBotsByOwner(ctx context.Context, ownerUserID pgtype.UUID)
|
|||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
@@ -266,7 +270,7 @@ UPDATE bots
|
|||||||
SET owner_user_id = $2,
|
SET owner_user_id = $2,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE id = $1
|
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 {
|
type UpdateBotOwnerParams struct {
|
||||||
@@ -286,6 +290,7 @@ func (q *Queries) UpdateBotOwner(ctx context.Context, arg UpdateBotOwnerParams)
|
|||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
@@ -307,7 +312,7 @@ SET display_name = $2,
|
|||||||
metadata = $5,
|
metadata = $5,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE id = $1
|
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 {
|
type UpdateBotProfileParams struct {
|
||||||
@@ -336,6 +341,7 @@ func (q *Queries) UpdateBotProfile(ctx context.Context, arg UpdateBotProfilePara
|
|||||||
&i.IsActive,
|
&i.IsActive,
|
||||||
&i.Status,
|
&i.Status,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
|
|||||||
@@ -590,7 +590,7 @@ WITH updated AS (
|
|||||||
SET display_name = $1,
|
SET display_name = $1,
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE bots.id = $2
|
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
|
SELECT
|
||||||
updated.id AS id,
|
updated.id AS id,
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ INSERT INTO bot_history_messages (
|
|||||||
source_reply_to_message_id,
|
source_reply_to_message_id,
|
||||||
role,
|
role,
|
||||||
content,
|
content,
|
||||||
metadata
|
metadata,
|
||||||
|
usage
|
||||||
)
|
)
|
||||||
VALUES (
|
VALUES (
|
||||||
$1,
|
$1,
|
||||||
@@ -34,7 +35,8 @@ VALUES (
|
|||||||
$7::text,
|
$7::text,
|
||||||
$8,
|
$8,
|
||||||
$9,
|
$9,
|
||||||
$10
|
$10,
|
||||||
|
$11
|
||||||
)
|
)
|
||||||
RETURNING
|
RETURNING
|
||||||
id,
|
id,
|
||||||
@@ -48,6 +50,7 @@ RETURNING
|
|||||||
role,
|
role,
|
||||||
content,
|
content,
|
||||||
metadata,
|
metadata,
|
||||||
|
usage,
|
||||||
created_at
|
created_at
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -62,6 +65,7 @@ type CreateMessageParams struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
Metadata []byte `json:"metadata"`
|
Metadata []byte `json:"metadata"`
|
||||||
|
Usage []byte `json:"usage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateMessageRow struct {
|
type CreateMessageRow struct {
|
||||||
@@ -76,6 +80,7 @@ type CreateMessageRow struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
Metadata []byte `json:"metadata"`
|
Metadata []byte `json:"metadata"`
|
||||||
|
Usage []byte `json:"usage"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +96,7 @@ func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) (C
|
|||||||
arg.Role,
|
arg.Role,
|
||||||
arg.Content,
|
arg.Content,
|
||||||
arg.Metadata,
|
arg.Metadata,
|
||||||
|
arg.Usage,
|
||||||
)
|
)
|
||||||
var i CreateMessageRow
|
var i CreateMessageRow
|
||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
@@ -105,6 +111,7 @@ func (q *Queries) CreateMessage(ctx context.Context, arg CreateMessageParams) (C
|
|||||||
&i.Role,
|
&i.Role,
|
||||||
&i.Content,
|
&i.Content,
|
||||||
&i.Metadata,
|
&i.Metadata,
|
||||||
|
&i.Usage,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
@@ -133,6 +140,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
@@ -155,6 +163,7 @@ type ListMessagesRow struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
Metadata []byte `json:"metadata"`
|
Metadata []byte `json:"metadata"`
|
||||||
|
Usage []byte `json:"usage"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
||||||
SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"`
|
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.Role,
|
||||||
&i.Content,
|
&i.Content,
|
||||||
&i.Metadata,
|
&i.Metadata,
|
||||||
|
&i.Usage,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.SenderDisplayName,
|
&i.SenderDisplayName,
|
||||||
&i.SenderAvatarUrl,
|
&i.SenderAvatarUrl,
|
||||||
@@ -208,6 +218,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
@@ -237,6 +248,7 @@ type ListMessagesBeforeRow struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
Metadata []byte `json:"metadata"`
|
Metadata []byte `json:"metadata"`
|
||||||
|
Usage []byte `json:"usage"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
||||||
SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"`
|
SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"`
|
||||||
@@ -263,6 +275,7 @@ func (q *Queries) ListMessagesBefore(ctx context.Context, arg ListMessagesBefore
|
|||||||
&i.Role,
|
&i.Role,
|
||||||
&i.Content,
|
&i.Content,
|
||||||
&i.Metadata,
|
&i.Metadata,
|
||||||
|
&i.Usage,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.SenderDisplayName,
|
&i.SenderDisplayName,
|
||||||
&i.SenderAvatarUrl,
|
&i.SenderAvatarUrl,
|
||||||
@@ -290,6 +303,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
@@ -317,6 +331,7 @@ type ListMessagesLatestRow struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
Metadata []byte `json:"metadata"`
|
Metadata []byte `json:"metadata"`
|
||||||
|
Usage []byte `json:"usage"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
||||||
SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"`
|
SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"`
|
||||||
@@ -343,6 +358,7 @@ func (q *Queries) ListMessagesLatest(ctx context.Context, arg ListMessagesLatest
|
|||||||
&i.Role,
|
&i.Role,
|
||||||
&i.Content,
|
&i.Content,
|
||||||
&i.Metadata,
|
&i.Metadata,
|
||||||
|
&i.Usage,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.SenderDisplayName,
|
&i.SenderDisplayName,
|
||||||
&i.SenderAvatarUrl,
|
&i.SenderAvatarUrl,
|
||||||
@@ -370,6 +386,7 @@ SELECT
|
|||||||
m.role,
|
m.role,
|
||||||
m.content,
|
m.content,
|
||||||
m.metadata,
|
m.metadata,
|
||||||
|
m.usage,
|
||||||
m.created_at,
|
m.created_at,
|
||||||
ci.display_name AS sender_display_name,
|
ci.display_name AS sender_display_name,
|
||||||
ci.avatar_url AS sender_avatar_url
|
ci.avatar_url AS sender_avatar_url
|
||||||
@@ -397,6 +414,7 @@ type ListMessagesSinceRow struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
Metadata []byte `json:"metadata"`
|
Metadata []byte `json:"metadata"`
|
||||||
|
Usage []byte `json:"usage"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
SenderDisplayName pgtype.Text `json:"sender_display_name"`
|
||||||
SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"`
|
SenderAvatarUrl pgtype.Text `json:"sender_avatar_url"`
|
||||||
@@ -423,6 +441,7 @@ func (q *Queries) ListMessagesSince(ctx context.Context, arg ListMessagesSincePa
|
|||||||
&i.Role,
|
&i.Role,
|
||||||
&i.Content,
|
&i.Content,
|
||||||
&i.Metadata,
|
&i.Metadata,
|
||||||
|
&i.Usage,
|
||||||
&i.CreatedAt,
|
&i.CreatedAt,
|
||||||
&i.SenderDisplayName,
|
&i.SenderDisplayName,
|
||||||
&i.SenderAvatarUrl,
|
&i.SenderAvatarUrl,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type Bot struct {
|
|||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
||||||
|
MaxContextTokens int32 `json:"max_context_tokens"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
AllowGuest bool `json:"allow_guest"`
|
AllowGuest bool `json:"allow_guest"`
|
||||||
ChatModelID pgtype.UUID `json:"chat_model_id"`
|
ChatModelID pgtype.UUID `json:"chat_model_id"`
|
||||||
@@ -69,6 +70,7 @@ type BotHistoryMessage struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content []byte `json:"content"`
|
Content []byte `json:"content"`
|
||||||
Metadata []byte `json:"metadata"`
|
Metadata []byte `json:"metadata"`
|
||||||
|
Usage []byte `json:"usage"`
|
||||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
const deleteSettingsByBotID = `-- name: DeleteSettingsByBotID :exec
|
const deleteSettingsByBotID = `-- name: DeleteSettingsByBotID :exec
|
||||||
UPDATE bots
|
UPDATE bots
|
||||||
SET max_context_load_time = 1440,
|
SET max_context_load_time = 1440,
|
||||||
|
max_context_tokens = 0,
|
||||||
language = 'auto',
|
language = 'auto',
|
||||||
allow_guest = false,
|
allow_guest = false,
|
||||||
chat_model_id = NULL,
|
chat_model_id = NULL,
|
||||||
@@ -33,6 +34,7 @@ const getSettingsByBotID = `-- name: GetSettingsByBotID :one
|
|||||||
SELECT
|
SELECT
|
||||||
bots.id AS bot_id,
|
bots.id AS bot_id,
|
||||||
bots.max_context_load_time,
|
bots.max_context_load_time,
|
||||||
|
bots.max_context_tokens,
|
||||||
bots.language,
|
bots.language,
|
||||||
bots.allow_guest,
|
bots.allow_guest,
|
||||||
chat_models.model_id AS chat_model_id,
|
chat_models.model_id AS chat_model_id,
|
||||||
@@ -50,6 +52,7 @@ WHERE bots.id = $1
|
|||||||
type GetSettingsByBotIDRow struct {
|
type GetSettingsByBotIDRow struct {
|
||||||
BotID pgtype.UUID `json:"bot_id"`
|
BotID pgtype.UUID `json:"bot_id"`
|
||||||
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
||||||
|
MaxContextTokens int32 `json:"max_context_tokens"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
AllowGuest bool `json:"allow_guest"`
|
AllowGuest bool `json:"allow_guest"`
|
||||||
ChatModelID pgtype.Text `json:"chat_model_id"`
|
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(
|
err := row.Scan(
|
||||||
&i.BotID,
|
&i.BotID,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
@@ -78,19 +82,21 @@ const upsertBotSettings = `-- name: UpsertBotSettings :one
|
|||||||
WITH updated AS (
|
WITH updated AS (
|
||||||
UPDATE bots
|
UPDATE bots
|
||||||
SET max_context_load_time = $1,
|
SET max_context_load_time = $1,
|
||||||
language = $2,
|
max_context_tokens = $2,
|
||||||
allow_guest = $3,
|
language = $3,
|
||||||
chat_model_id = COALESCE($4::uuid, bots.chat_model_id),
|
allow_guest = $4,
|
||||||
memory_model_id = COALESCE($5::uuid, bots.memory_model_id),
|
chat_model_id = COALESCE($5::uuid, bots.chat_model_id),
|
||||||
embedding_model_id = COALESCE($6::uuid, bots.embedding_model_id),
|
memory_model_id = COALESCE($6::uuid, bots.memory_model_id),
|
||||||
search_provider_id = COALESCE($7::uuid, bots.search_provider_id),
|
embedding_model_id = COALESCE($7::uuid, bots.embedding_model_id),
|
||||||
|
search_provider_id = COALESCE($8::uuid, bots.search_provider_id),
|
||||||
updated_at = now()
|
updated_at = now()
|
||||||
WHERE bots.id = $8
|
WHERE bots.id = $9
|
||||||
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
|
SELECT
|
||||||
updated.id AS bot_id,
|
updated.id AS bot_id,
|
||||||
updated.max_context_load_time,
|
updated.max_context_load_time,
|
||||||
|
updated.max_context_tokens,
|
||||||
updated.language,
|
updated.language,
|
||||||
updated.allow_guest,
|
updated.allow_guest,
|
||||||
chat_models.model_id AS chat_model_id,
|
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 {
|
type UpsertBotSettingsParams struct {
|
||||||
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
||||||
|
MaxContextTokens int32 `json:"max_context_tokens"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
AllowGuest bool `json:"allow_guest"`
|
AllowGuest bool `json:"allow_guest"`
|
||||||
ChatModelID pgtype.UUID `json:"chat_model_id"`
|
ChatModelID pgtype.UUID `json:"chat_model_id"`
|
||||||
@@ -118,6 +125,7 @@ type UpsertBotSettingsParams struct {
|
|||||||
type UpsertBotSettingsRow struct {
|
type UpsertBotSettingsRow struct {
|
||||||
BotID pgtype.UUID `json:"bot_id"`
|
BotID pgtype.UUID `json:"bot_id"`
|
||||||
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
MaxContextLoadTime int32 `json:"max_context_load_time"`
|
||||||
|
MaxContextTokens int32 `json:"max_context_tokens"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
AllowGuest bool `json:"allow_guest"`
|
AllowGuest bool `json:"allow_guest"`
|
||||||
ChatModelID pgtype.Text `json:"chat_model_id"`
|
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) {
|
func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsParams) (UpsertBotSettingsRow, error) {
|
||||||
row := q.db.QueryRow(ctx, upsertBotSettings,
|
row := q.db.QueryRow(ctx, upsertBotSettings,
|
||||||
arg.MaxContextLoadTime,
|
arg.MaxContextLoadTime,
|
||||||
|
arg.MaxContextTokens,
|
||||||
arg.Language,
|
arg.Language,
|
||||||
arg.AllowGuest,
|
arg.AllowGuest,
|
||||||
arg.ChatModelID,
|
arg.ChatModelID,
|
||||||
@@ -141,6 +150,7 @@ func (q *Queries) UpsertBotSettings(ctx context.Context, arg UpsertBotSettingsPa
|
|||||||
err := row.Scan(
|
err := row.Scan(
|
||||||
&i.BotID,
|
&i.BotID,
|
||||||
&i.MaxContextLoadTime,
|
&i.MaxContextLoadTime,
|
||||||
|
&i.MaxContextTokens,
|
||||||
&i.Language,
|
&i.Language,
|
||||||
&i.AllowGuest,
|
&i.AllowGuest,
|
||||||
&i.ChatModelID,
|
&i.ChatModelID,
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ func (s *DBService) Persist(ctx context.Context, input PersistInput) (Message, e
|
|||||||
Role: input.Role,
|
Role: input.Role,
|
||||||
Content: content,
|
Content: content,
|
||||||
Metadata: metaBytes,
|
Metadata: metaBytes,
|
||||||
|
Usage: input.Usage,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Message{}, err
|
return Message{}, err
|
||||||
@@ -213,6 +214,7 @@ func toMessageFromCreate(row sqlc.CreateMessageRow) Message {
|
|||||||
row.Role,
|
row.Role,
|
||||||
row.Content,
|
row.Content,
|
||||||
row.Metadata,
|
row.Metadata,
|
||||||
|
row.Usage,
|
||||||
row.CreatedAt,
|
row.CreatedAt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -232,6 +234,7 @@ func toMessageFromListRow(row sqlc.ListMessagesRow) Message {
|
|||||||
row.Role,
|
row.Role,
|
||||||
row.Content,
|
row.Content,
|
||||||
row.Metadata,
|
row.Metadata,
|
||||||
|
row.Usage,
|
||||||
row.CreatedAt,
|
row.CreatedAt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -251,6 +254,7 @@ func toMessageFromSinceRow(row sqlc.ListMessagesSinceRow) Message {
|
|||||||
row.Role,
|
row.Role,
|
||||||
row.Content,
|
row.Content,
|
||||||
row.Metadata,
|
row.Metadata,
|
||||||
|
row.Usage,
|
||||||
row.CreatedAt,
|
row.CreatedAt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -270,6 +274,7 @@ func toMessageFromLatestRow(row sqlc.ListMessagesLatestRow) Message {
|
|||||||
row.Role,
|
row.Role,
|
||||||
row.Content,
|
row.Content,
|
||||||
row.Metadata,
|
row.Metadata,
|
||||||
|
row.Usage,
|
||||||
row.CreatedAt,
|
row.CreatedAt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -288,6 +293,7 @@ func toMessageFields(
|
|||||||
role string,
|
role string,
|
||||||
content []byte,
|
content []byte,
|
||||||
metadata []byte,
|
metadata []byte,
|
||||||
|
usage []byte,
|
||||||
createdAt pgtype.Timestamptz,
|
createdAt pgtype.Timestamptz,
|
||||||
) Message {
|
) Message {
|
||||||
return Message{
|
return Message{
|
||||||
@@ -304,6 +310,7 @@ func toMessageFields(
|
|||||||
Role: role,
|
Role: role,
|
||||||
Content: json.RawMessage(content),
|
Content: json.RawMessage(content),
|
||||||
Metadata: parseJSONMap(metadata),
|
Metadata: parseJSONMap(metadata),
|
||||||
|
Usage: json.RawMessage(usage),
|
||||||
CreatedAt: createdAt.Time,
|
CreatedAt: createdAt.Time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,6 +354,7 @@ func toMessageFromBeforeRow(row sqlc.ListMessagesBeforeRow) Message {
|
|||||||
row.Role,
|
row.Role,
|
||||||
row.Content,
|
row.Content,
|
||||||
row.Metadata,
|
row.Metadata,
|
||||||
|
row.Usage,
|
||||||
row.CreatedAt,
|
row.CreatedAt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ type Message struct {
|
|||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
Content json.RawMessage `json:"content"`
|
Content json.RawMessage `json:"content"`
|
||||||
Metadata map[string]any `json:"metadata,omitempty"`
|
Metadata map[string]any `json:"metadata,omitempty"`
|
||||||
|
Usage json.RawMessage `json:"usage,omitempty"`
|
||||||
Assets []MessageAsset `json:"assets,omitempty"`
|
Assets []MessageAsset `json:"assets,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,7 @@ type PersistInput struct {
|
|||||||
Role string
|
Role string
|
||||||
Content json.RawMessage
|
Content json.RawMessage
|
||||||
Metadata map[string]any
|
Metadata map[string]any
|
||||||
|
Usage json.RawMessage
|
||||||
Assets []AssetRef
|
Assets []AssetRef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,10 +54,13 @@ func (s *Service) UpsertBot(ctx context.Context, botID string, req UpsertRequest
|
|||||||
}
|
}
|
||||||
isPersonalBot := strings.EqualFold(strings.TrimSpace(botRow.Type), "personal")
|
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 {
|
if req.MaxContextLoadTime != nil && *req.MaxContextLoadTime > 0 {
|
||||||
current.MaxContextLoadTime = *req.MaxContextLoadTime
|
current.MaxContextLoadTime = *req.MaxContextLoadTime
|
||||||
}
|
}
|
||||||
|
if req.MaxContextTokens != nil && *req.MaxContextTokens >= 0 {
|
||||||
|
current.MaxContextTokens = *req.MaxContextTokens
|
||||||
|
}
|
||||||
if strings.TrimSpace(req.Language) != "" {
|
if strings.TrimSpace(req.Language) != "" {
|
||||||
current.Language = 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{
|
updated, err := s.queries.UpsertBotSettings(ctx, sqlc.UpsertBotSettingsParams{
|
||||||
ID: pgID,
|
ID: pgID,
|
||||||
MaxContextLoadTime: int32(current.MaxContextLoadTime),
|
MaxContextLoadTime: int32(current.MaxContextLoadTime),
|
||||||
|
MaxContextTokens: int32(current.MaxContextTokens),
|
||||||
Language: current.Language,
|
Language: current.Language,
|
||||||
AllowGuest: current.AllowGuest,
|
AllowGuest: current.AllowGuest,
|
||||||
ChatModelID: chatModelUUID,
|
ChatModelID: chatModelUUID,
|
||||||
@@ -130,15 +134,19 @@ func (s *Service) Delete(ctx context.Context, botID string) error {
|
|||||||
return s.queries.DeleteSettingsByBotID(ctx, pgID)
|
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{
|
settings := Settings{
|
||||||
MaxContextLoadTime: int(maxContextLoadTime),
|
MaxContextLoadTime: int(maxContextLoadTime),
|
||||||
|
MaxContextTokens: int(maxContextTokens),
|
||||||
Language: strings.TrimSpace(language),
|
Language: strings.TrimSpace(language),
|
||||||
AllowGuest: allowGuest,
|
AllowGuest: allowGuest,
|
||||||
}
|
}
|
||||||
if settings.MaxContextLoadTime <= 0 {
|
if settings.MaxContextLoadTime <= 0 {
|
||||||
settings.MaxContextLoadTime = DefaultMaxContextLoadTime
|
settings.MaxContextLoadTime = DefaultMaxContextLoadTime
|
||||||
}
|
}
|
||||||
|
if settings.MaxContextTokens < 0 {
|
||||||
|
settings.MaxContextTokens = 0
|
||||||
|
}
|
||||||
if settings.Language == "" {
|
if settings.Language == "" {
|
||||||
settings.Language = DefaultLanguage
|
settings.Language = DefaultLanguage
|
||||||
}
|
}
|
||||||
@@ -148,6 +156,7 @@ func normalizeBotSetting(maxContextLoadTime int32, language string, allowGuest b
|
|||||||
func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings {
|
func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings {
|
||||||
return normalizeBotSettingsFields(
|
return normalizeBotSettingsFields(
|
||||||
row.MaxContextLoadTime,
|
row.MaxContextLoadTime,
|
||||||
|
row.MaxContextTokens,
|
||||||
row.Language,
|
row.Language,
|
||||||
row.AllowGuest,
|
row.AllowGuest,
|
||||||
row.ChatModelID,
|
row.ChatModelID,
|
||||||
@@ -160,6 +169,7 @@ func normalizeBotSettingsReadRow(row sqlc.GetSettingsByBotIDRow) Settings {
|
|||||||
func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings {
|
func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings {
|
||||||
return normalizeBotSettingsFields(
|
return normalizeBotSettingsFields(
|
||||||
row.MaxContextLoadTime,
|
row.MaxContextLoadTime,
|
||||||
|
row.MaxContextTokens,
|
||||||
row.Language,
|
row.Language,
|
||||||
row.AllowGuest,
|
row.AllowGuest,
|
||||||
row.ChatModelID,
|
row.ChatModelID,
|
||||||
@@ -171,6 +181,7 @@ func normalizeBotSettingsWriteRow(row sqlc.UpsertBotSettingsRow) Settings {
|
|||||||
|
|
||||||
func normalizeBotSettingsFields(
|
func normalizeBotSettingsFields(
|
||||||
maxContextLoadTime int32,
|
maxContextLoadTime int32,
|
||||||
|
maxContextTokens int32,
|
||||||
language string,
|
language string,
|
||||||
allowGuest bool,
|
allowGuest bool,
|
||||||
chatModelID pgtype.Text,
|
chatModelID pgtype.Text,
|
||||||
@@ -178,7 +189,7 @@ func normalizeBotSettingsFields(
|
|||||||
embeddingModelID pgtype.Text,
|
embeddingModelID pgtype.Text,
|
||||||
searchProviderID pgtype.UUID,
|
searchProviderID pgtype.UUID,
|
||||||
) Settings {
|
) Settings {
|
||||||
settings := normalizeBotSetting(maxContextLoadTime, language, allowGuest)
|
settings := normalizeBotSetting(maxContextLoadTime, maxContextTokens, language, allowGuest)
|
||||||
settings.ChatModelID = strings.TrimSpace(chatModelID.String)
|
settings.ChatModelID = strings.TrimSpace(chatModelID.String)
|
||||||
settings.MemoryModelID = strings.TrimSpace(memoryModelID.String)
|
settings.MemoryModelID = strings.TrimSpace(memoryModelID.String)
|
||||||
settings.EmbeddingModelID = strings.TrimSpace(embeddingModelID.String)
|
settings.EmbeddingModelID = strings.TrimSpace(embeddingModelID.String)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type Settings struct {
|
|||||||
EmbeddingModelID string `json:"embedding_model_id"`
|
EmbeddingModelID string `json:"embedding_model_id"`
|
||||||
SearchProviderID string `json:"search_provider_id"`
|
SearchProviderID string `json:"search_provider_id"`
|
||||||
MaxContextLoadTime int `json:"max_context_load_time"`
|
MaxContextLoadTime int `json:"max_context_load_time"`
|
||||||
|
MaxContextTokens int `json:"max_context_tokens"`
|
||||||
Language string `json:"language"`
|
Language string `json:"language"`
|
||||||
AllowGuest bool `json:"allow_guest"`
|
AllowGuest bool `json:"allow_guest"`
|
||||||
}
|
}
|
||||||
@@ -21,6 +22,7 @@ type UpsertRequest struct {
|
|||||||
EmbeddingModelID string `json:"embedding_model_id,omitempty"`
|
EmbeddingModelID string `json:"embedding_model_id,omitempty"`
|
||||||
SearchProviderID string `json:"search_provider_id,omitempty"`
|
SearchProviderID string `json:"search_provider_id,omitempty"`
|
||||||
MaxContextLoadTime *int `json:"max_context_load_time,omitempty"`
|
MaxContextLoadTime *int `json:"max_context_load_time,omitempty"`
|
||||||
|
MaxContextTokens *int `json:"max_context_tokens,omitempty"`
|
||||||
Language string `json:"language,omitempty"`
|
Language string `json:"language,omitempty"`
|
||||||
AllowGuest *bool `json:"allow_guest,omitempty"`
|
AllowGuest *bool `json:"allow_guest,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -673,6 +673,7 @@ export type MessageMessage = {
|
|||||||
sender_display_name?: string;
|
sender_display_name?: string;
|
||||||
sender_user_id?: string;
|
sender_user_id?: string;
|
||||||
source_reply_to_message_id?: string;
|
source_reply_to_message_id?: string;
|
||||||
|
usage?: Array<number>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MessageMessageAsset = {
|
export type MessageMessageAsset = {
|
||||||
@@ -866,6 +867,7 @@ export type SettingsSettings = {
|
|||||||
embedding_model_id?: string;
|
embedding_model_id?: string;
|
||||||
language?: string;
|
language?: string;
|
||||||
max_context_load_time?: number;
|
max_context_load_time?: number;
|
||||||
|
max_context_tokens?: number;
|
||||||
memory_model_id?: string;
|
memory_model_id?: string;
|
||||||
search_provider_id?: string;
|
search_provider_id?: string;
|
||||||
};
|
};
|
||||||
@@ -876,6 +878,7 @@ export type SettingsUpsertRequest = {
|
|||||||
embedding_model_id?: string;
|
embedding_model_id?: string;
|
||||||
language?: string;
|
language?: string;
|
||||||
max_context_load_time?: number;
|
max_context_load_time?: number;
|
||||||
|
max_context_tokens?: number;
|
||||||
memory_model_id?: string;
|
memory_model_id?: string;
|
||||||
search_provider_id?: string;
|
search_provider_id?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -354,6 +354,7 @@
|
|||||||
"searchProvider": "Search Provider",
|
"searchProvider": "Search Provider",
|
||||||
"searchProviderPlaceholder": "Select search provider",
|
"searchProviderPlaceholder": "Select search provider",
|
||||||
"maxContextLoadTime": "Max Context Load Time",
|
"maxContextLoadTime": "Max Context Load Time",
|
||||||
|
"maxContextTokens": "Max Context Tokens",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"allowGuest": "Allow Guest Access",
|
"allowGuest": "Allow Guest Access",
|
||||||
"allowGuestPersonalHint": "Personal bots do not support guest access. Use a public bot instead.",
|
"allowGuestPersonalHint": "Personal bots do not support guest access. Use a public bot instead.",
|
||||||
|
|||||||
@@ -350,6 +350,7 @@
|
|||||||
"searchProvider": "搜索提供方",
|
"searchProvider": "搜索提供方",
|
||||||
"searchProviderPlaceholder": "选择搜索提供方",
|
"searchProviderPlaceholder": "选择搜索提供方",
|
||||||
"maxContextLoadTime": "最大上下文加载时间",
|
"maxContextLoadTime": "最大上下文加载时间",
|
||||||
|
"maxContextTokens": "最大上下文Token数",
|
||||||
"language": "语言",
|
"language": "语言",
|
||||||
"allowGuest": "允许游客访问",
|
"allowGuest": "允许游客访问",
|
||||||
"allowGuestPersonalHint": "个人 Bot 不支持游客访问,请使用公开 Bot。",
|
"allowGuestPersonalHint": "个人 Bot 不支持游客访问,请使用公开 Bot。",
|
||||||
|
|||||||
@@ -58,6 +58,17 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Max Context Tokens -->
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label>{{ $t('bots.settings.maxContextTokens') }}</Label>
|
||||||
|
<Input
|
||||||
|
v-model.number="form.max_context_tokens"
|
||||||
|
type="number"
|
||||||
|
:min="0"
|
||||||
|
placeholder="0"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Language -->
|
<!-- Language -->
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<Label>{{ $t('bots.settings.language') }}</Label>
|
<Label>{{ $t('bots.settings.language') }}</Label>
|
||||||
@@ -226,6 +237,7 @@ const form = reactive<SettingsSettings>({
|
|||||||
embedding_model_id: '',
|
embedding_model_id: '',
|
||||||
search_provider_id: '',
|
search_provider_id: '',
|
||||||
max_context_load_time: 0,
|
max_context_load_time: 0,
|
||||||
|
max_context_tokens: 0,
|
||||||
language: '',
|
language: '',
|
||||||
allow_guest: false,
|
allow_guest: false,
|
||||||
})
|
})
|
||||||
@@ -238,6 +250,7 @@ watch(settings, (val) => {
|
|||||||
form.embedding_model_id = val.embedding_model_id ?? ''
|
form.embedding_model_id = val.embedding_model_id ?? ''
|
||||||
form.search_provider_id = val.search_provider_id ?? ''
|
form.search_provider_id = val.search_provider_id ?? ''
|
||||||
form.max_context_load_time = val.max_context_load_time ?? 0
|
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.language = val.language ?? ''
|
||||||
form.allow_guest = val.allow_guest ?? false
|
form.allow_guest = val.allow_guest ?? false
|
||||||
}
|
}
|
||||||
@@ -252,6 +265,7 @@ const hasChanges = computed(() => {
|
|||||||
|| form.embedding_model_id !== (s.embedding_model_id ?? '')
|
|| form.embedding_model_id !== (s.embedding_model_id ?? '')
|
||||||
|| form.search_provider_id !== (s.search_provider_id ?? '')
|
|| form.search_provider_id !== (s.search_provider_id ?? '')
|
||||||
|| form.max_context_load_time !== (s.max_context_load_time ?? 0)
|
|| form.max_context_load_time !== (s.max_context_load_time ?? 0)
|
||||||
|
|| form.max_context_tokens !== (s.max_context_tokens ?? 0)
|
||||||
|| form.language !== (s.language ?? '')
|
|| form.language !== (s.language ?? '')
|
||||||
if (isPublicBot.value) {
|
if (isPublicBot.value) {
|
||||||
changed = changed || form.allow_guest !== (s.allow_guest ?? false)
|
changed = changed || form.allow_guest !== (s.allow_guest ?? false)
|
||||||
|
|||||||
@@ -6451,6 +6451,12 @@ const docTemplate = `{
|
|||||||
},
|
},
|
||||||
"source_reply_to_message_id": {
|
"source_reply_to_message_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"usage": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -6949,6 +6955,9 @@ const docTemplate = `{
|
|||||||
"max_context_load_time": {
|
"max_context_load_time": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"max_context_tokens": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memory_model_id": {
|
"memory_model_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -6975,6 +6984,9 @@ const docTemplate = `{
|
|||||||
"max_context_load_time": {
|
"max_context_load_time": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"max_context_tokens": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memory_model_id": {
|
"memory_model_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6442,6 +6442,12 @@
|
|||||||
},
|
},
|
||||||
"source_reply_to_message_id": {
|
"source_reply_to_message_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"usage": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -6940,6 +6946,9 @@
|
|||||||
"max_context_load_time": {
|
"max_context_load_time": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"max_context_tokens": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memory_model_id": {
|
"memory_model_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -6966,6 +6975,9 @@
|
|||||||
"max_context_load_time": {
|
"max_context_load_time": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"max_context_tokens": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"memory_model_id": {
|
"memory_model_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1101,6 +1101,10 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
source_reply_to_message_id:
|
source_reply_to_message_id:
|
||||||
type: string
|
type: string
|
||||||
|
usage:
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
|
type: array
|
||||||
type: object
|
type: object
|
||||||
message.MessageAsset:
|
message.MessageAsset:
|
||||||
properties:
|
properties:
|
||||||
@@ -1435,6 +1439,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
max_context_load_time:
|
max_context_load_time:
|
||||||
type: integer
|
type: integer
|
||||||
|
max_context_tokens:
|
||||||
|
type: integer
|
||||||
memory_model_id:
|
memory_model_id:
|
||||||
type: string
|
type: string
|
||||||
search_provider_id:
|
search_provider_id:
|
||||||
@@ -1452,6 +1458,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
max_context_load_time:
|
max_context_load_time:
|
||||||
type: integer
|
type: integer
|
||||||
|
max_context_tokens:
|
||||||
|
type: integer
|
||||||
memory_model_id:
|
memory_model_id:
|
||||||
type: string
|
type: string
|
||||||
search_provider_id:
|
search_provider_id:
|
||||||
|
|||||||
Reference in New Issue
Block a user