Skip to content

Commit 9c35755

Browse files
author
mesut
committed
add google tools
1 parent 5837161 commit 9c35755

5 files changed

Lines changed: 274 additions & 24 deletions

File tree

apps/backend/src/services/tool.service.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { toolRepository } from '../repositories/tool.repository';
2-
import { Tool, CreateToolRequest, UpdateToolRequest, ToolType, ApiToolConfig } from '../types/tool';
2+
import { Tool, CreateToolRequest, UpdateToolRequest, ToolType, ApiToolConfig, GoogleSheetsToolConfig } from '../types/tool';
33
import axios from 'axios';
44

55
export class ToolService {
@@ -23,8 +23,19 @@ export class ToolService {
2323
}
2424
}
2525

26+
// Validate Google Sheets tool requirements
27+
if (data.type === 'google_sheets_tool') {
28+
const config = data.config as GoogleSheetsToolConfig;
29+
if (!config || !config.spreadsheetId || !config.range) {
30+
throw new Error('Google Sheets tools must have a config with spreadsheetId and range');
31+
}
32+
}
33+
2634
try {
27-
const isSystem = data.type !== 'api-request';
35+
const isSystem = data.type !== 'api-request' &&
36+
data.type !== 'google_calendar_event_create' &&
37+
data.type !== 'google_calendar_check_availability_tool' &&
38+
data.type !== 'google_sheets_tool';
2839
return await toolRepository.create(data, userId, isSystem, false);
2940
} catch (error: any) {
3041
// Handle duplicate name error

apps/backend/src/types/tool.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type ToolType = 'end-call' | 'api-request' | 'voicemail-detection';
1+
export type ToolType = 'end-call' | 'api-request' | 'voicemail-detection' | 'google_calendar_event_create' | 'google_calendar_check_availability_tool' | 'google_sheets_tool';
22
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
33

44
// Parameter definition for tools (LLM function calling)
@@ -25,13 +25,25 @@ export interface SystemToolConfig {
2525
parameters?: ToolParameter[]; // Parameters for system tools if needed
2626
}
2727

28+
// Configuration for Google Calendar tools
29+
export interface GoogleCalendarToolConfig {
30+
calendarId?: string; // Defaults to 'primary'
31+
timezone?: string; // Defaults to UTC
32+
}
33+
34+
// Configuration for Google Sheets tool
35+
export interface GoogleSheetsToolConfig {
36+
spreadsheetId: string;
37+
range: string; // e.g., Sheet1!A:Z
38+
}
39+
2840
export interface Tool {
2941
id: string;
3042
userId: string;
3143
name: string;
3244
type: ToolType;
3345
description: string;
34-
config?: ApiToolConfig | SystemToolConfig; // Flexible config based on type
46+
config?: ApiToolConfig | SystemToolConfig | GoogleCalendarToolConfig | GoogleSheetsToolConfig; // Flexible config based on type
3547
isSystem: boolean;
3648
isDefault: boolean;
3749
createdAt: Date;
@@ -42,11 +54,11 @@ export interface CreateToolRequest {
4254
name: string;
4355
type: ToolType;
4456
description: string;
45-
config?: ApiToolConfig | SystemToolConfig;
57+
config?: ApiToolConfig | SystemToolConfig | GoogleCalendarToolConfig | GoogleSheetsToolConfig;
4658
}
4759

4860
export interface UpdateToolRequest {
4961
name?: string;
5062
description?: string;
51-
config?: ApiToolConfig | SystemToolConfig;
63+
config?: ApiToolConfig | SystemToolConfig | GoogleCalendarToolConfig | GoogleSheetsToolConfig;
5264
}

apps/frontend/src/i18n/tr.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,16 @@
288288
"deleteConfirm": "Bu aracı silmek istediğinizden emin misiniz?",
289289
"deleteSuccess": "Araç başarıyla silindi",
290290
"deleteError": "Araç silinirken hata oluştu",
291+
"googleCalendar": "Google Calendar Etkinliği Oluştur",
292+
"googleCalendarDesc": "Google Takvim'de etkinlik oluşturun",
293+
"checkAvailability": "Google Calendar Uygunluk Kontrolü",
294+
"checkAvailabilityDesc": "Takvim müsaitliğini kontrol edin",
295+
"googleSheets": "Google Sheets",
296+
"googleSheetsDesc": "Google Sheets'e veri okuyun ve yazın",
297+
"calendarSettings" : "Takvim Ayarları",
298+
"calendarSettingsDesc": "Etkinlik oluşturmak için Google Takvim'i yapılandırın",
299+
"googleSheetsSettings": "Google Sheets Ayarları",
300+
"googleSheetsSettingsDesc": "Bu araç için Google Sheet ve aralığı yapılandırın",
291301
"form": {
292302
"name": "Araç Adı",
293303
"namePlaceholder": "Aracınıza bir isim verin",

apps/frontend/src/pages/Tools/ToolsPage.tsx

Lines changed: 219 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
22
import { useNavigate, useParams } from 'react-router-dom';
33
import { useTranslation } from 'react-i18next';
44
import { toolsApi } from '../../api/client';
5-
import type { Tool, CreateToolRequest, ApiToolConfig, HttpMethod } from '../../types/tool';
5+
import type { Tool, CreateToolRequest, ApiToolConfig, HttpMethod, ToolType, GoogleCalendarToolConfig, GoogleSheetsToolConfig } from '../../types/tool';
66
import './ToolsPage.css';
77

88
const ToolsPage: React.FC = () => {
@@ -81,25 +81,59 @@ const ToolsPage: React.FC = () => {
8181
setShowTypeDropdown(!showTypeDropdown);
8282
};
8383

84-
const selectToolType = (type: 'api-request') => {
84+
const selectToolType = (type: ToolType) => {
8585
setShowTypeDropdown(false);
8686
setShowTypeSelection(false);
8787
setSelectedTool(null);
8888
setIsEditing(true);
89-
const defaultBody = {};
89+
90+
let config: any;
91+
let defaultBody = {};
92+
let defaultName = '';
93+
94+
switch (type) {
95+
case 'api-request':
96+
config = {
97+
url: '',
98+
method: 'POST',
99+
headers: {},
100+
body: defaultBody,
101+
authToken: '',
102+
parameters: []
103+
};
104+
defaultName = '';
105+
break;
106+
case 'google_calendar_event_create':
107+
config = {
108+
calendarId: '',
109+
timezone: 'America/New_York'
110+
};
111+
defaultName = 'google_calendar_event_create';
112+
break;
113+
case 'google_calendar_check_availability_tool':
114+
config = {
115+
calendarId: '',
116+
timezone: 'America/New_York'
117+
};
118+
defaultName = 'google_calendar_check_availability_tool';
119+
break;
120+
case 'google_sheets_tool':
121+
config = {
122+
spreadsheetId: '',
123+
range: ''
124+
};
125+
defaultName = 'google_sheets_tool';
126+
break;
127+
default:
128+
config = {};
129+
defaultName = '';
130+
}
90131

91132
setFormData({
92-
name: '',
133+
name: defaultName,
93134
type: type,
94135
description: '',
95-
config: {
96-
url: '',
97-
method: 'POST',
98-
headers: {},
99-
body: defaultBody,
100-
authToken: '',
101-
parameters: []
102-
}
136+
config
103137
});
104138
setJsonBody(JSON.stringify(defaultBody, null, 2));
105139
setTestResult(null);
@@ -269,6 +303,22 @@ const ToolsPage: React.FC = () => {
269303
}
270304
};
271305

306+
const isFormValid = () => {
307+
if (!formData.name || !formData.description) {
308+
return false;
309+
}
310+
311+
// Validate Google Sheets required fields
312+
if (formData.type === 'google_sheets_tool') {
313+
const config = formData.config as GoogleSheetsToolConfig;
314+
if (!config?.spreadsheetId || !config?.range) {
315+
return false;
316+
}
317+
}
318+
319+
return true;
320+
};
321+
272322
return (
273323
<div className="tools-page-container">
274324
{/* Left Sidebar - Tools List */}
@@ -280,7 +330,7 @@ const ToolsPage: React.FC = () => {
280330
</button>
281331
{showTypeDropdown && (
282332
<div className="tool-type-dropdown">
283-
<button
333+
<button
284334
className="tool-type-option"
285335
onClick={() => selectToolType('api-request')}
286336
>
@@ -290,6 +340,36 @@ const ToolsPage: React.FC = () => {
290340
<span className="option-desc">{t('tools.typeDescriptions.apiRequest')}</span>
291341
</div>
292342
</button>
343+
<button
344+
className="tool-type-option"
345+
onClick={() => selectToolType('google_calendar_event_create')}
346+
>
347+
<span className="option-icon">📅</span>
348+
<div className="option-info">
349+
<span className="option-title">{t('tools.googleCalendar')}</span>
350+
<span className="option-desc">{t('tools.googleCalendarDesc')}</span>
351+
</div>
352+
</button>
353+
<button
354+
className="tool-type-option"
355+
onClick={() => selectToolType('google_calendar_check_availability_tool')}
356+
>
357+
<span className="option-icon">🗓️</span>
358+
<div className="option-info">
359+
<span className="option-title">{t('tools.checkAvailability')}</span>
360+
<span className="option-desc">{t('tools.checkAvailabilityDesc')}</span>
361+
</div>
362+
</button>
363+
<button
364+
className="tool-type-option"
365+
onClick={() => selectToolType('google_sheets_tool')}
366+
>
367+
<span className="option-icon">📊</span>
368+
<div className="option-info">
369+
<span className="option-title">{t('tools.googleSheets')}</span>
370+
<span className="option-desc">{t('tools.googleSheetsDesc')}</span>
371+
</div>
372+
</button>
293373
</div>
294374
)}
295375
</div>
@@ -394,7 +474,7 @@ const ToolsPage: React.FC = () => {
394474
type="button"
395475
className="btn-header-primary"
396476
onClick={handleSave}
397-
disabled={saving || !formData.name || !formData.description}
477+
disabled={saving || !isFormValid()}
398478
>
399479
{saving ? t('tools.form.saving') : t('tools.form.save')}
400480
</button>
@@ -701,6 +781,131 @@ const ToolsPage: React.FC = () => {
701781
</>
702782
)}
703783

784+
{/* Google Calendar Tool Configuration */}
785+
{formData.type === 'google_calendar_event_create' && (
786+
<div className="form-section">
787+
<h3 className="section-title">
788+
<span className="section-icon">📅</span>
789+
{t('tools.calendarSettings')}
790+
</h3>
791+
<p className="section-desc">{t('tools.calendarSettingsDesc')}</p>
792+
793+
<div className="form-group">
794+
<label>Calendar ID</label>
795+
<input
796+
type="text"
797+
className="form-input"
798+
placeholder="Enter Calendar ID"
799+
value={(formData.config as GoogleCalendarToolConfig)?.calendarId || ''}
800+
onChange={(e) => setFormData({
801+
...formData,
802+
config: { ...formData.config as GoogleCalendarToolConfig, calendarId: e.target.value }
803+
})}
804+
/>
805+
<p className="form-help">The ID of the Google Calendar. Defaults to "primary" if not specified.</p>
806+
</div>
807+
808+
<div className="form-group">
809+
<label>Timezone</label>
810+
<input
811+
type="text"
812+
className="form-input"
813+
placeholder="America/New_York"
814+
value={(formData.config as GoogleCalendarToolConfig)?.timezone || 'America/New_York'}
815+
onChange={(e) => setFormData({
816+
...formData,
817+
config: { ...formData.config as GoogleCalendarToolConfig, timezone: e.target.value }
818+
})}
819+
/>
820+
<p className="form-help">The time zone for the calendar event (e.g., America/New_York). Defaults to UTC if not specified.</p>
821+
</div>
822+
</div>
823+
)}
824+
825+
{/* Google Calendar Check Availability Tool Configuration */}
826+
{formData.type === 'google_calendar_check_availability_tool' && (
827+
<div className="form-section">
828+
<h3 className="section-title">
829+
<span className="section-icon">🗓️</span>
830+
{t('tools.calendarSettings')}
831+
</h3>
832+
<p className="section-desc">{t('tools.checkAvailabilitySettings')}</p>
833+
834+
<div className="form-group">
835+
<label>Calendar ID</label>
836+
<input
837+
type="text"
838+
className="form-input"
839+
placeholder="Enter Calendar ID"
840+
value={(formData.config as GoogleCalendarToolConfig)?.calendarId || ''}
841+
onChange={(e) => setFormData({
842+
...formData,
843+
config: { ...formData.config as GoogleCalendarToolConfig, calendarId: e.target.value }
844+
})}
845+
/>
846+
<p className="form-help">The ID of the Google Calendar to check. Defaults to "primary" if not specified.</p>
847+
</div>
848+
849+
<div className="form-group">
850+
<label>Timezone</label>
851+
<input
852+
type="text"
853+
className="form-input"
854+
placeholder="America/New_York"
855+
value={(formData.config as GoogleCalendarToolConfig)?.timezone || 'America/New_York'}
856+
onChange={(e) => setFormData({
857+
...formData,
858+
config: { ...formData.config as GoogleCalendarToolConfig, timezone: e.target.value }
859+
})}
860+
/>
861+
<p className="form-help">The time zone for the availability check (e.g., America/New_York). Defaults to UTC if not specified.</p>
862+
</div>
863+
</div>
864+
)}
865+
866+
{/* Google Sheets Tool Configuration */}
867+
{formData.type === 'google_sheets_tool' && (
868+
<div className="form-section">
869+
<h3 className="section-title">
870+
<span className="section-icon">📊</span>
871+
{t('tools.googleSheetsSettings')}
872+
</h3>
873+
<p className="section-desc">{t('tools.googleSheetsSettingsDesc')}</p>
874+
875+
<div className="form-group">
876+
<label>Spreadsheet ID <span className="required">*</span></label>
877+
<input
878+
type="text"
879+
className="form-input"
880+
placeholder="Enter Spreadsheet ID"
881+
value={(formData.config as GoogleSheetsToolConfig)?.spreadsheetId || ''}
882+
onChange={(e) => setFormData({
883+
...formData,
884+
config: { ...formData.config as GoogleSheetsToolConfig, spreadsheetId: e.target.value }
885+
})}
886+
required
887+
/>
888+
<p className="form-help">The ID of the Google Sheet to append data to</p>
889+
</div>
890+
891+
<div className="form-group">
892+
<label>Range <span className="required">*</span></label>
893+
<input
894+
type="text"
895+
className="form-input"
896+
placeholder="Enter Range (e.g., Sheet1!A:Z)"
897+
value={(formData.config as GoogleSheetsToolConfig)?.range || ''}
898+
onChange={(e) => setFormData({
899+
...formData,
900+
config: { ...formData.config as GoogleSheetsToolConfig, range: e.target.value }
901+
})}
902+
required
903+
/>
904+
<p className="form-help">The range where the data should be appended (e.g., Sheet1, Sheet1!A:Z)</p>
905+
</div>
906+
</div>
907+
)}
908+
704909
{/* Actions */}
705910
<div className="form-actions">
706911
{selectedTool && !selectedTool.isDefault && (

0 commit comments

Comments
 (0)