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
12 changes: 7 additions & 5 deletions internal/adapters/ai/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"time"

"github.com/nylas/cli/internal/domain"
"github.com/nylas/cli/internal/httputil"
)

// maxErrorBodyBytes caps how much of an error response body is read into
Expand All @@ -28,17 +29,18 @@ type BaseClient struct {

// NewBaseClient creates a new base client with common configuration.
func NewBaseClient(apiKey, model, baseURL string, timeout time.Duration) *BaseClient {
if timeout == 0 {
timeout = domain.TimeoutAI
// The default (timeout == 0) reuses the shared 120s client; a non-zero
// timeout gets its own client.
client := httputil.DefaultClient
if timeout != 0 {
client = httputil.NewClient(timeout)
}

return &BaseClient{
apiKey: apiKey,
model: model,
baseURL: baseURL,
client: &http.Client{
Timeout: timeout,
},
client: client,
}
}

Expand Down
56 changes: 0 additions & 56 deletions internal/adapters/ai/function_tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,59 +176,3 @@ func GetSchedulingTools() []domain.Tool {
},
}
}

// GetSmartSchedulingTools returns additional tools for advanced AI scheduling features.
func GetSmartSchedulingTools() []domain.Tool {
return []domain.Tool{
{
Name: "analyzeMeetingContext",
Description: "Analyze the context of a meeting request to determine priority, optimal duration, and required participants based on historical patterns.",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"meetingType": map[string]any{
"type": "string",
"description": "Type of meeting (e.g., '1-on-1', 'team', 'planning', 'client')",
},
"participants": map[string]any{
"type": "array",
"description": "Participant email addresses",
"items": map[string]any{
"type": "string",
},
},
"subject": map[string]any{
"type": "string",
"description": "Meeting subject/topic",
},
},
"required": []string{"meetingType"},
},
},
{
Name: "suggestRotatingSchedule",
Description: "Suggest a rotating meeting schedule for recurring meetings with participants across multiple timezones to ensure fairness.",
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"participants": map[string]any{
"type": "array",
"description": "Participant email addresses with their timezones",
"items": map[string]any{
"type": "string",
},
},
"duration": map[string]any{
"type": "integer",
"description": "Meeting duration in minutes",
},
"frequency": map[string]any{
"type": "string",
"description": "Meeting frequency (e.g., 'weekly', 'biweekly', 'monthly')",
},
},
"required": []string{"participants", "duration", "frequency"},
},
},
}
}
130 changes: 0 additions & 130 deletions internal/adapters/ai/function_tools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,136 +303,6 @@ func TestGetSchedulingTools_GetTimezoneInfo(t *testing.T) {
assert.Len(t, required, 1)
}

func TestGetSmartSchedulingTools(t *testing.T) {
t.Parallel()

tools := GetSmartSchedulingTools()

require.NotEmpty(t, tools)
assert.Len(t, tools, 2, "Should have 2 smart scheduling tools")

// Verify expected tool names
expectedNames := map[string]bool{
"analyzeMeetingContext": false,
"suggestRotatingSchedule": false,
}

for _, tool := range tools {
assert.NotEmpty(t, tool.Name)
assert.NotEmpty(t, tool.Description)
assert.NotNil(t, tool.Parameters)

if _, exists := expectedNames[tool.Name]; exists {
expectedNames[tool.Name] = true
}
}

// Verify all expected tools were found
for name, found := range expectedNames {
assert.True(t, found, "Expected tool %s not found", name)
}
}

func TestGetSmartSchedulingTools_AnalyzeMeetingContext(t *testing.T) {
t.Parallel()

tools := GetSmartSchedulingTools()

var analyzeMeetingContext *struct {
Name string
Description string
Parameters map[string]any
}

for i := range tools {
if tools[i].Name == "analyzeMeetingContext" {
analyzeMeetingContext = &struct {
Name string
Description string
Parameters map[string]any
}{
Name: tools[i].Name,
Description: tools[i].Description,
Parameters: tools[i].Parameters,
}
break
}
}

require.NotNil(t, analyzeMeetingContext)

// Check description
assert.Contains(t, analyzeMeetingContext.Description, "context")
assert.Contains(t, analyzeMeetingContext.Description, "priority")

// Check parameters
params := analyzeMeetingContext.Parameters
properties, ok := params["properties"].(map[string]any)
require.True(t, ok)

// Check meetingType parameter
meetingType, ok := properties["meetingType"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "string", meetingType["type"])
assert.Contains(t, meetingType["description"].(string), "1-on-1")

// Check required only includes meetingType
required, ok := params["required"].([]string)
require.True(t, ok)
assert.Contains(t, required, "meetingType")
}

func TestGetSmartSchedulingTools_SuggestRotatingSchedule(t *testing.T) {
t.Parallel()

tools := GetSmartSchedulingTools()

var suggestRotatingSchedule *struct {
Name string
Description string
Parameters map[string]any
}

for i := range tools {
if tools[i].Name == "suggestRotatingSchedule" {
suggestRotatingSchedule = &struct {
Name string
Description string
Parameters map[string]any
}{
Name: tools[i].Name,
Description: tools[i].Description,
Parameters: tools[i].Parameters,
}
break
}
}

require.NotNil(t, suggestRotatingSchedule)

// Check description
assert.Contains(t, suggestRotatingSchedule.Description, "rotating")
assert.Contains(t, suggestRotatingSchedule.Description, "fairness")

// Check parameters
params := suggestRotatingSchedule.Parameters
properties, ok := params["properties"].(map[string]any)
require.True(t, ok)

// Check frequency parameter
frequency, ok := properties["frequency"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "string", frequency["type"])
assert.Contains(t, frequency["description"].(string), "weekly")

// Check required fields
required, ok := params["required"].([]string)
require.True(t, ok)
assert.Contains(t, required, "participants")
assert.Contains(t, required, "duration")
assert.Contains(t, required, "frequency")
}

func TestToolParameterTypes(t *testing.T) {
t.Parallel()

Expand Down
Loading
Loading