优化路由
This commit is contained in:
@@ -0,0 +1,83 @@
|
|||||||
|
package agents
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FunctionToolSchema struct {
|
||||||
|
Name string
|
||||||
|
Description string
|
||||||
|
Parameters map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionToolRuntime struct {
|
||||||
|
UserID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func FunctionToolSchemas(configs []ToolConfig) []FunctionToolSchema {
|
||||||
|
tools := make([]FunctionToolSchema, 0)
|
||||||
|
for _, config := range configs {
|
||||||
|
if !config.Enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch strings.ToLower(strings.TrimSpace(config.Name)) {
|
||||||
|
case "time":
|
||||||
|
tools = append(tools, timeFunctionToolSchema())
|
||||||
|
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
||||||
|
tools = append(tools, opsAIAssistantScheduleQuerySchema())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tools
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExecuteFunctionTool(ctx context.Context, runtime FunctionToolRuntime, name string, rawArgs []byte) ([]byte, error) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
switch strings.ToLower(strings.TrimSpace(name)) {
|
||||||
|
case "time":
|
||||||
|
var args TimeRangeArgs
|
||||||
|
if len(rawArgs) > 0 {
|
||||||
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, err := ResolveTimeRange(args, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(result)
|
||||||
|
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
||||||
|
return executeOpsAIAssistantScheduleQuery(ctx, runtime, rawArgs)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown tool: %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeFunctionToolSchema() FunctionToolSchema {
|
||||||
|
return FunctionToolSchema{
|
||||||
|
Name: "time",
|
||||||
|
Description: "解析当前时间、相对日期和日期范围。遇到本月、今天、本周等相对日期时先调用本工具获得明确日期。",
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]interface{}{
|
||||||
|
"range": map[string]interface{}{
|
||||||
|
"type": "string",
|
||||||
|
"enum": []string{"today", "yesterday", "tomorrow", "this_week", "last_week", "next_week", "this_month", "last_month", "next_month", "this_year", "custom"},
|
||||||
|
"description": "要解析的日期范围。",
|
||||||
|
},
|
||||||
|
"timezone": map[string]interface{}{"type": "string", "description": "可选时区,例如 Asia/Shanghai。"},
|
||||||
|
"start_date": map[string]interface{}{"type": "string", "description": "custom 范围开始日期,格式 YYYY-MM-DD。"},
|
||||||
|
"end_date": map[string]interface{}{"type": "string", "description": "custom 范围结束日期,格式 YYYY-MM-DD。"},
|
||||||
|
},
|
||||||
|
"required": []string{"range"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package agents
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScheduleQueryArgs struct {
|
||||||
|
StartDate string `json:"start_date"`
|
||||||
|
EndDate string `json:"end_date"`
|
||||||
|
CalendarID uint `json:"calendar_id,omitempty"`
|
||||||
|
Limit int `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleCalendar struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleEvent struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
CalendarID uint `json:"calendar_id"`
|
||||||
|
UserID uint `json:"user_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
StartDate string `json:"start_date"`
|
||||||
|
EndDate string `json:"end_date"`
|
||||||
|
ScheduleType string `json:"schedule_type"`
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
|
Remark string `json:"remark,omitempty"`
|
||||||
|
CanEdit bool `json:"canEdit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleQuery struct {
|
||||||
|
StartDate string
|
||||||
|
EndDate string
|
||||||
|
CalendarID uint
|
||||||
|
UserID uint
|
||||||
|
Limit int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScheduleProvider interface {
|
||||||
|
QuerySchedules(ctx context.Context, query ScheduleQuery) ([]ScheduleEvent, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var registeredScheduleProvider ScheduleProvider = nil
|
||||||
|
|
||||||
|
func RegisterScheduleProvider(provider ScheduleProvider) {
|
||||||
|
registeredScheduleProvider = provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func opsAIAssistantScheduleQuerySchema() FunctionToolSchema {
|
||||||
|
return FunctionToolSchema{
|
||||||
|
Name: "ops_ai_assistant_schedule_query",
|
||||||
|
Description: "按明确日期范围查询当前用户可见的 OPS 日历/日程。相对日期需先调用 time 获取 start_date/end_date。",
|
||||||
|
Parameters: map[string]interface{}{
|
||||||
|
"type": "object",
|
||||||
|
"properties": map[string]interface{}{
|
||||||
|
"start_date": map[string]interface{}{"type": "string", "description": "开始日期,格式 YYYY-MM-DD。"},
|
||||||
|
"end_date": map[string]interface{}{"type": "string", "description": "结束日期,格式 YYYY-MM-DD。"},
|
||||||
|
"calendar_id": map[string]interface{}{"type": "integer", "description": "可选日历 ID;不传则查询全部可见日程。"},
|
||||||
|
"limit": map[string]interface{}{"type": "integer", "description": "可选返回上限,默认 100,最大 200。"},
|
||||||
|
},
|
||||||
|
"required": []string{"start_date", "end_date"},
|
||||||
|
},
|
||||||
|
// ScheduleQueryResult is the return type packed as JSON
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeOpsAIAssistantScheduleQuery(ctx context.Context, runtime FunctionToolRuntime, rawArgs []byte) ([]byte, error) {
|
||||||
|
var args ScheduleQueryArgs
|
||||||
|
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
startDate, err := time.Parse("2006-01-02", args.StartDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid start_date: %w", err)
|
||||||
|
}
|
||||||
|
endDate, err := time.Parse("2006-01-02", args.EndDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid end_date: %w", err)
|
||||||
|
}
|
||||||
|
if endDate.Before(startDate) {
|
||||||
|
return nil, fmt.Errorf("end_date must be after start_date")
|
||||||
|
}
|
||||||
|
limit := args.Limit
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 100
|
||||||
|
}
|
||||||
|
if limit > 200 {
|
||||||
|
limit = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
if registeredScheduleProvider == nil {
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": false,
|
||||||
|
"error": "日程查询服务未注册",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
events, err := registeredScheduleProvider.QuerySchedules(ctx, ScheduleQuery{
|
||||||
|
StartDate: args.StartDate,
|
||||||
|
EndDate: args.EndDate,
|
||||||
|
CalendarID: args.CalendarID,
|
||||||
|
UserID: runtime.UserID,
|
||||||
|
Limit: limit,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if events == nil {
|
||||||
|
events = []ScheduleEvent{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(map[string]interface{}{
|
||||||
|
"ok": true,
|
||||||
|
"start_date": args.StartDate,
|
||||||
|
"end_date": args.EndDate,
|
||||||
|
"count": len(events),
|
||||||
|
"limit": limit,
|
||||||
|
"events": events,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -888,53 +888,17 @@ func buildToolConfigs(configs []models.ConfigsAIChatTool_) []agents.ToolConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildFunctionTools(configs []agents.ToolConfig) []openaiTool {
|
func buildFunctionTools(configs []agents.ToolConfig) []openaiTool {
|
||||||
tools := make([]openaiTool, 0)
|
schemas := agents.FunctionToolSchemas(configs)
|
||||||
for _, config := range configs {
|
tools := make([]openaiTool, 0, len(schemas))
|
||||||
if !config.Enabled {
|
for _, schema := range schemas {
|
||||||
continue
|
tools = append(tools, openaiTool{
|
||||||
}
|
Type: "function",
|
||||||
switch strings.ToLower(strings.TrimSpace(config.Name)) {
|
Function: openaiFunctionDefinition{
|
||||||
case "time":
|
Name: schema.Name,
|
||||||
tools = append(tools, openaiTool{
|
Description: schema.Description,
|
||||||
Type: "function",
|
Parameters: schema.Parameters,
|
||||||
Function: openaiFunctionDefinition{
|
},
|
||||||
Name: "time",
|
})
|
||||||
Description: "解析当前时间、相对日期和日期范围。遇到本月、今天、本周等相对日期时先调用本工具获得明确日期。",
|
|
||||||
Parameters: map[string]interface{}{
|
|
||||||
"type": "object",
|
|
||||||
"properties": map[string]interface{}{
|
|
||||||
"range": map[string]interface{}{
|
|
||||||
"type": "string",
|
|
||||||
"enum": []string{"today", "yesterday", "tomorrow", "this_week", "last_week", "next_week", "this_month", "last_month", "next_month", "this_year", "custom"},
|
|
||||||
"description": "要解析的日期范围。",
|
|
||||||
},
|
|
||||||
"timezone": map[string]interface{}{"type": "string", "description": "可选时区,例如 Asia/Shanghai。"},
|
|
||||||
"start_date": map[string]interface{}{"type": "string", "description": "custom 范围开始日期,格式 YYYY-MM-DD。"},
|
|
||||||
"end_date": map[string]interface{}{"type": "string", "description": "custom 范围结束日期,格式 YYYY-MM-DD。"},
|
|
||||||
},
|
|
||||||
"required": []string{"range"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
|
||||||
tools = append(tools, openaiTool{
|
|
||||||
Type: "function",
|
|
||||||
Function: openaiFunctionDefinition{
|
|
||||||
Name: "ops_ai_assistant_schedule_query",
|
|
||||||
Description: "按明确日期范围查询当前用户可见的 OPS 日历/日程。相对日期需先调用 time 获取 start_date/end_date。",
|
|
||||||
Parameters: map[string]interface{}{
|
|
||||||
"type": "object",
|
|
||||||
"properties": map[string]interface{}{
|
|
||||||
"start_date": map[string]interface{}{"type": "string", "description": "开始日期,格式 YYYY-MM-DD。"},
|
|
||||||
"end_date": map[string]interface{}{"type": "string", "description": "结束日期,格式 YYYY-MM-DD。"},
|
|
||||||
"calendar_id": map[string]interface{}{"type": "integer", "description": "可选日历 ID;不传则查询全部可见日程。"},
|
|
||||||
"limit": map[string]interface{}{"type": "integer", "description": "可选返回上限,默认 100,最大 200。"},
|
|
||||||
},
|
|
||||||
"required": []string{"start_date", "end_date"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return tools
|
return tools
|
||||||
}
|
}
|
||||||
@@ -1015,74 +979,12 @@ func runOpenAIToolLoop(ctx context.Context, profile models.ConfigsAIChatOpenAI_,
|
|||||||
return messages, toolExecuted, fmt.Errorf("工具调用超过最大轮数")
|
return messages, toolExecuted, fmt.Errorf("工具调用超过最大轮数")
|
||||||
}
|
}
|
||||||
|
|
||||||
type scheduleQueryArgs struct {
|
|
||||||
StartDate string `json:"start_date"`
|
|
||||||
EndDate string `json:"end_date"`
|
|
||||||
CalendarID uint `json:"calendar_id,omitempty"`
|
|
||||||
Limit int `json:"limit,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func executeAIFunctionTool(ctx context.Context, name string, rawArgs []byte, currentUser *TabUser) ([]byte, error) {
|
func executeAIFunctionTool(ctx context.Context, name string, rawArgs []byte, currentUser *TabUser) ([]byte, error) {
|
||||||
select {
|
runtime := agents.FunctionToolRuntime{}
|
||||||
case <-ctx.Done():
|
if currentUser != nil {
|
||||||
return nil, ctx.Err()
|
runtime.UserID = currentUser.ID
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
switch name {
|
|
||||||
case "time":
|
|
||||||
var args agents.TimeRangeArgs
|
|
||||||
if len(rawArgs) > 0 {
|
|
||||||
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result, err := agents.ResolveTimeRange(args, time.Now())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return json.Marshal(result)
|
|
||||||
case "ops_ai_assistant_schedule_query", "ops_ai_assistant":
|
|
||||||
var args scheduleQueryArgs
|
|
||||||
if err := json.Unmarshal(rawArgs, &args); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
startDate, err := time.Parse("2006-01-02", args.StartDate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid start_date: %w", err)
|
|
||||||
}
|
|
||||||
endDate, err := time.Parse("2006-01-02", args.EndDate)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid end_date: %w", err)
|
|
||||||
}
|
|
||||||
limit := args.Limit
|
|
||||||
if limit <= 0 {
|
|
||||||
limit = 100
|
|
||||||
}
|
|
||||||
if limit > 200 {
|
|
||||||
limit = 200
|
|
||||||
}
|
|
||||||
events, err := QueryCalendarSchedulesForAI(CalendarScheduleQuery{
|
|
||||||
CalendarID: args.CalendarID,
|
|
||||||
StartDate: startDate,
|
|
||||||
EndDate: endDate,
|
|
||||||
User: currentUser,
|
|
||||||
Limit: limit,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return json.Marshal(map[string]interface{}{
|
|
||||||
"ok": true,
|
|
||||||
"start_date": args.StartDate,
|
|
||||||
"end_date": args.EndDate,
|
|
||||||
"count": len(events),
|
|
||||||
"limit": limit,
|
|
||||||
"events": events,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown tool: %s", name)
|
|
||||||
}
|
}
|
||||||
|
return agents.ExecuteFunctionTool(ctx, runtime, name, rawArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectOpenAIProfile(cfg models.ConfigsAIChat_, name string) (models.ConfigsAIChatOpenAI_, bool) {
|
func selectOpenAIProfile(cfg models.ConfigsAIChat_, name string) (models.ConfigsAIChatOpenAI_, bool) {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package routers
|
package routers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"ops/agents"
|
||||||
"ops/models"
|
"ops/models"
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
@@ -141,6 +143,57 @@ type CalendarScheduleEvent struct {
|
|||||||
CanEdit bool `json:"canEdit"`
|
CanEdit bool `json:"canEdit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type calendarScheduleProvider struct{}
|
||||||
|
|
||||||
|
func (calendarScheduleProvider) QuerySchedules(ctx context.Context, query agents.ScheduleQuery) ([]agents.ScheduleEvent, error) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
startDate, err := time.Parse("2006-01-02", query.StartDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
endDate, err := time.Parse("2006-01-02", query.EndDate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var user *TabUser
|
||||||
|
if query.UserID > 0 {
|
||||||
|
user = &TabUser{ID: query.UserID}
|
||||||
|
}
|
||||||
|
events, err := QueryCalendarSchedulesForAI(CalendarScheduleQuery{
|
||||||
|
CalendarID: query.CalendarID,
|
||||||
|
StartDate: startDate,
|
||||||
|
EndDate: endDate,
|
||||||
|
User: user,
|
||||||
|
Limit: query.Limit,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]agents.ScheduleEvent, 0, len(events))
|
||||||
|
for _, event := range events {
|
||||||
|
result = append(result, agents.ScheduleEvent{
|
||||||
|
ID: event.ID,
|
||||||
|
CalendarID: event.CalendarID,
|
||||||
|
UserID: event.UserID,
|
||||||
|
Title: event.Title,
|
||||||
|
StartDate: event.StartDate,
|
||||||
|
EndDate: event.EndDate,
|
||||||
|
ScheduleType: event.ScheduleType,
|
||||||
|
IsPublic: event.IsPublic,
|
||||||
|
Remark: event.Remark,
|
||||||
|
CanEdit: event.CanEdit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func QueryCalendarSchedulesForAI(query CalendarScheduleQuery) ([]CalendarScheduleEvent, error) {
|
func QueryCalendarSchedulesForAI(query CalendarScheduleQuery) ([]CalendarScheduleEvent, error) {
|
||||||
startDate := dateOnly(query.StartDate)
|
startDate := dateOnly(query.StartDate)
|
||||||
endDate := dateOnly(query.EndDate)
|
endDate := dateOnly(query.EndDate)
|
||||||
@@ -261,6 +314,7 @@ func ApiCalendarInit() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
CalendarUpdateAdminsCash()
|
CalendarUpdateAdminsCash()
|
||||||
|
agents.RegisterScheduleProvider(calendarScheduleProvider{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApiCalendar(r *gin.RouterGroup) {
|
func ApiCalendar(r *gin.RouterGroup) {
|
||||||
|
|||||||
Reference in New Issue
Block a user