Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions agent/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,18 @@ export const ClientTypeModel = z.enum([
'openai-responses', 'openai-completions', 'anthropic-messages', 'google-generative-ai',
])

export const ReasoningConfigModel = z.object({
enabled: z.boolean(),
effort: z.enum(['low', 'medium', 'high']),
}).optional()

export const ModelConfigModel = z.object({
modelId: z.string().min(1, 'Model ID is required'),
clientType: ClientTypeModel,
input: z.array(z.enum(['text', 'image', 'audio', 'video', 'file'])),
apiKey: z.string().min(1, 'API key is required'),
baseUrl: z.string(),
reasoning: ReasoningConfigModel,
})

export const AllowedActionModel = z.enum(allActions)
Expand Down
6 changes: 5 additions & 1 deletion db/migrations/0001_init.up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ CREATE TABLE IF NOT EXISTS models (
client_type TEXT,
dimensions INTEGER,
input_modalities TEXT[] NOT NULL DEFAULT ARRAY['text']::TEXT[],
supports_reasoning BOOLEAN NOT NULL DEFAULT false,
type TEXT NOT NULL DEFAULT 'chat',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
Expand Down Expand Up @@ -121,6 +122,8 @@ CREATE TABLE IF NOT EXISTS bots (
max_context_tokens INTEGER NOT NULL DEFAULT 0,
language TEXT NOT NULL DEFAULT 'auto',
allow_guest BOOLEAN NOT NULL DEFAULT false,
reasoning_enabled BOOLEAN NOT NULL DEFAULT false,
reasoning_effort TEXT NOT NULL DEFAULT 'medium',
max_inbox_items INTEGER NOT NULL DEFAULT 50,
chat_model_id UUID REFERENCES models(id) ON DELETE SET NULL,
memory_model_id UUID REFERENCES models(id) ON DELETE SET NULL,
Expand All @@ -130,7 +133,8 @@ CREATE TABLE IF NOT EXISTS bots (
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
CONSTRAINT bots_type_check CHECK (type IN ('personal', 'public')),
CONSTRAINT bots_status_check CHECK (status IN ('creating', 'ready', 'deleting'))
CONSTRAINT bots_status_check CHECK (status IN ('creating', 'ready', 'deleting')),
CONSTRAINT bots_reasoning_effort_check CHECK (reasoning_effort IN ('low', 'medium', 'high'))
);

CREATE INDEX IF NOT EXISTS idx_bots_owner_user_id ON bots(owner_user_id);
Expand Down
8 changes: 8 additions & 0 deletions db/migrations/0014_reasoning.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- 0014_reasoning (rollback)
-- Remove reasoning support flag from models and reasoning settings from bots.

ALTER TABLE bots DROP CONSTRAINT IF EXISTS bots_reasoning_effort_check;
ALTER TABLE bots DROP COLUMN IF EXISTS reasoning_effort;
ALTER TABLE bots DROP COLUMN IF EXISTS reasoning_enabled;

ALTER TABLE models DROP COLUMN IF EXISTS supports_reasoning;
17 changes: 17 additions & 0 deletions db/migrations/0014_reasoning.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- 0014_reasoning
-- Add reasoning support flag to models and reasoning settings to bots.

ALTER TABLE models ADD COLUMN IF NOT EXISTS supports_reasoning BOOLEAN NOT NULL DEFAULT false;

ALTER TABLE bots ADD COLUMN IF NOT EXISTS reasoning_enabled BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE bots ADD COLUMN IF NOT EXISTS reasoning_effort TEXT NOT NULL DEFAULT 'medium';
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'bots_reasoning_effort_check'
) THEN
ALTER TABLE bots ADD CONSTRAINT bots_reasoning_effort_check
CHECK (reasoning_effort IN ('low', 'medium', 'high'));
END IF;
END
$$;
12 changes: 6 additions & 6 deletions db/queries/bots.sql
Original file line number Diff line number Diff line change
@@ -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, max_context_tokens, max_inbox_items, 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, max_inbox_items, language, allow_guest, reasoning_enabled, reasoning_effort, 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, max_context_tokens, max_inbox_items, 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, max_inbox_items, language, allow_guest, reasoning_enabled, reasoning_effort, 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, max_context_tokens, max_inbox_items, 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, max_inbox_items, language, allow_guest, reasoning_enabled, reasoning_effort, 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.max_context_tokens, b.max_inbox_items, 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.max_inbox_items, b.language, b.allow_guest, b.reasoning_enabled, b.reasoning_effort, 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
Expand All @@ -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, max_context_tokens, max_inbox_items, 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, max_inbox_items, language, allow_guest, reasoning_enabled, reasoning_effort, 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, max_context_tokens, max_inbox_items, 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, max_inbox_items, language, allow_guest, reasoning_enabled, reasoning_effort, chat_model_id, memory_model_id, embedding_model_id, search_provider_id, metadata, created_at, updated_at;

-- name: UpdateBotStatus :exec
UPDATE bots
Expand Down
5 changes: 4 additions & 1 deletion db/queries/models.sql
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@ DELETE FROM llm_providers WHERE id = sqlc.arg(id);
SELECT COUNT(*) FROM llm_providers;

-- name: CreateModel :one
INSERT INTO models (model_id, name, llm_provider_id, client_type, dimensions, input_modalities, type)
INSERT INTO models (model_id, name, llm_provider_id, client_type, dimensions, input_modalities, supports_reasoning, type)
VALUES (
sqlc.arg(model_id),
sqlc.arg(name),
sqlc.arg(llm_provider_id),
sqlc.narg(client_type),
sqlc.arg(dimensions),
sqlc.arg(input_modalities),
sqlc.arg(supports_reasoning),
sqlc.arg(type)
)
RETURNING *;
Expand Down Expand Up @@ -93,6 +94,7 @@ SET
client_type = sqlc.narg(client_type),
dimensions = sqlc.arg(dimensions),
input_modalities = sqlc.arg(input_modalities),
supports_reasoning = sqlc.arg(supports_reasoning),
type = sqlc.arg(type),
updated_at = now()
WHERE id = sqlc.arg(id)
Expand All @@ -107,6 +109,7 @@ SET
client_type = sqlc.narg(client_type),
dimensions = sqlc.arg(dimensions),
input_modalities = sqlc.arg(input_modalities),
supports_reasoning = sqlc.arg(supports_reasoning),
type = sqlc.arg(type),
updated_at = now()
WHERE model_id = sqlc.arg(model_id)
Expand Down
10 changes: 9 additions & 1 deletion db/queries/settings.sql
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ SELECT
bots.max_inbox_items,
bots.language,
bots.allow_guest,
bots.reasoning_enabled,
bots.reasoning_effort,
chat_models.id AS chat_model_id,
memory_models.id AS memory_model_id,
embedding_models.id AS embedding_model_id,
Expand All @@ -25,13 +27,15 @@ WITH updated AS (
max_inbox_items = sqlc.arg(max_inbox_items),
language = sqlc.arg(language),
allow_guest = sqlc.arg(allow_guest),
reasoning_enabled = sqlc.arg(reasoning_enabled),
reasoning_effort = sqlc.arg(reasoning_effort),
chat_model_id = COALESCE(sqlc.narg(chat_model_id)::uuid, bots.chat_model_id),
memory_model_id = COALESCE(sqlc.narg(memory_model_id)::uuid, bots.memory_model_id),
embedding_model_id = COALESCE(sqlc.narg(embedding_model_id)::uuid, bots.embedding_model_id),
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.max_context_tokens, bots.max_inbox_items, 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.max_inbox_items, bots.language, bots.allow_guest, bots.reasoning_enabled, bots.reasoning_effort, bots.chat_model_id, bots.memory_model_id, bots.embedding_model_id, bots.search_provider_id
)
SELECT
updated.id AS bot_id,
Expand All @@ -40,6 +44,8 @@ SELECT
updated.max_inbox_items,
updated.language,
updated.allow_guest,
updated.reasoning_enabled,
updated.reasoning_effort,
chat_models.id AS chat_model_id,
memory_models.id AS memory_model_id,
embedding_models.id AS embedding_model_id,
Expand All @@ -57,6 +63,8 @@ SET max_context_load_time = 1440,
max_inbox_items = 50,
language = 'auto',
allow_guest = false,
reasoning_enabled = false,
reasoning_effort = 'medium',
chat_model_id = NULL,
memory_model_id = NULL,
embedding_model_id = NULL,
Expand Down
6 changes: 4 additions & 2 deletions internal/channel/adapters/feishu/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (s *feishuOutboundStream) Push(ctx context.Context, event channel.StreamEve
}
return nil
case channel.StreamEventDelta:
if event.Delta == "" {
if event.Delta == "" || event.Phase == channel.StreamPhaseReasoning {
return nil
}
s.textBuffer.WriteString(event.Delta)
Expand Down Expand Up @@ -96,7 +96,9 @@ func (s *feishuOutboundStream) Push(ctx context.Context, event channel.StreamEve
Target: s.target,
Message: media,
})
case channel.StreamEventAgentStart, channel.StreamEventAgentEnd, channel.StreamEventPhaseStart, channel.StreamEventPhaseEnd, channel.StreamEventProcessingStarted, channel.StreamEventProcessingCompleted, channel.StreamEventProcessingFailed:
case channel.StreamEventPhaseStart, channel.StreamEventPhaseEnd:
return nil
case channel.StreamEventAgentStart, channel.StreamEventAgentEnd, channel.StreamEventProcessingStarted, channel.StreamEventProcessingCompleted, channel.StreamEventProcessingFailed:
return nil
case channel.StreamEventFinal:
if event.Final == nil || event.Final.Message.IsEmpty() {
Expand Down
6 changes: 4 additions & 2 deletions internal/channel/adapters/telegram/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,12 @@ func (s *telegramOutboundStream) Push(ctx context.Context, event channel.StreamE
}
}
return nil
case channel.StreamEventProcessingFailed, channel.StreamEventAgentStart, channel.StreamEventAgentEnd, channel.StreamEventPhaseStart, channel.StreamEventPhaseEnd, channel.StreamEventProcessingStarted, channel.StreamEventProcessingCompleted:
case channel.StreamEventPhaseStart, channel.StreamEventPhaseEnd:
return nil
case channel.StreamEventProcessingFailed, channel.StreamEventAgentStart, channel.StreamEventAgentEnd, channel.StreamEventProcessingStarted, channel.StreamEventProcessingCompleted:
return nil
case channel.StreamEventDelta:
if event.Delta == "" {
if event.Delta == "" || event.Phase == channel.StreamPhaseReasoning {
return nil
}
s.mu.Lock()
Expand Down
25 changes: 20 additions & 5 deletions internal/conversation/flow/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,18 @@ func (r *Resolver) SetInboxService(service *inbox.Service) {

// --- gateway payload ---

type gatewayReasoningConfig struct {
Enabled bool `json:"enabled"`
Effort string `json:"effort"`
}

type gatewayModelConfig struct {
ModelID string `json:"modelId"`
ClientType string `json:"clientType"`
Input []string `json:"input"`
APIKey string `json:"apiKey"`
BaseURL string `json:"baseUrl"`
ModelID string `json:"modelId"`
ClientType string `json:"clientType"`
Input []string `json:"input"`
APIKey string `json:"apiKey"`
BaseURL string `json:"baseUrl"`
Reasoning *gatewayReasoningConfig `json:"reasoning,omitempty"`
}

type gatewayIdentity struct {
Expand Down Expand Up @@ -393,13 +399,22 @@ func (r *Resolver) resolve(ctx context.Context, req conversation.ChatRequest) (r
req.Query,
)

var reasoning *gatewayReasoningConfig
if chatModel.SupportsReasoning && botSettings.ReasoningEnabled {
reasoning = &gatewayReasoningConfig{
Enabled: true,
Effort: botSettings.ReasoningEffort,
}
}

payload := gatewayRequest{
Model: gatewayModelConfig{
ModelID: chatModel.ModelID,
ClientType: clientType,
Input: chatModel.InputModalities,
APIKey: provider.ApiKey,
BaseURL: provider.BaseUrl,
Reasoning: reasoning,
},
ActiveContextTime: maxCtx,
Channels: nonNilStrings(req.Channels),
Expand Down
Loading