Skip to content
Open
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
104 changes: 97 additions & 7 deletions wavefront/client/src/api/tool-service.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,112 @@
import { IApiResponse } from '@app/lib/axios';
import { ToolDetailsData, ToolDetailsResponse, ToolNamesData, ToolNamesResponse } from '@app/types/tool';
import {
VoiceAgentTool,
CreateToolRequest,
UpdateToolRequest,
AttachToolToAgentRequest,
UpdateAgentToolRequest,
ListToolsParams,
ToolResponse,
ToolDetailResponse,
ToolListResponse,
AgentToolsResponse,
ToolListData,
AgentToolsData,
ToolData,
} from '@app/types/tool';
import { AxiosInstance } from 'axios';

export class ToolService {
constructor(private http: AxiosInstance) {}

async getToolNames(): Promise<ToolNamesResponse> {
const response: IApiResponse<ToolNamesData> = await this.http.get(`/v1/:appId/floware/v1/tools/names`);
/**
* Create a new tool
*/
async createTool(data: CreateToolRequest): Promise<ToolResponse> {
const response: IApiResponse<ToolData> = await this.http.post(`/v1/:appId/floware/v1/tools`, data);
return response;
}

async getToolNamesAndDetails(): Promise<ToolNamesResponse> {
const response: IApiResponse<ToolNamesData> = await this.http.get(`/v1/:appId/floware/v1/tools/tool-details`);
/**
* Get a single tool by ID
*/
async getTool(toolId: string): Promise<ToolDetailResponse> {
const response: IApiResponse<VoiceAgentTool> = await this.http.get(`/v1/:appId/floware/v1/tools/${toolId}`);
return response;
}

async getToolDetails(toolName: string): Promise<ToolDetailsResponse> {
const response: IApiResponse<ToolDetailsData> = await this.http.get(`/v1/:appId/floware/v1/tools/${toolName}`);
/**
* List all tools with optional filters
*/
async listTools(params?: ListToolsParams): Promise<ToolListResponse> {
const response: IApiResponse<ToolListData> = await this.http.get(`/v1/:appId/floware/v1/tools`, { params });
return response;
}

/**
* Get tool names and details
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async getToolNamesAndDetails(): Promise<any> {
const response = await this.http.get(`/v1/:appId/floware/v1/tools/names`);
return response;
}

/**
* Update an existing tool
*/
async updateTool(toolId: string, data: UpdateToolRequest): Promise<ToolResponse> {
const response: IApiResponse<ToolData> = await this.http.patch(`/v1/:appId/floware/v1/tools/${toolId}`, data);
return response;
}

/**
* Delete a tool (soft delete)
*/
async deleteTool(toolId: string): Promise<ToolResponse> {
const response: IApiResponse<ToolData> = await this.http.delete(`/v1/:appId/floware/v1/tools/${toolId}`);
return response;
}

/**
* Attach a tool to a voice agent
*/
async attachToolToAgent(agentId: string, data: AttachToolToAgentRequest): Promise<ToolResponse> {
const response: IApiResponse<ToolData> = await this.http.post(
`/v1/:appId/floware/v1/voice-agents/${agentId}/tools`,
data
);
return response;
}

/**
* Get all tools attached to a voice agent
*/
async getAgentTools(agentId: string): Promise<AgentToolsResponse> {
const response: IApiResponse<AgentToolsData> = await this.http.get(
`/v1/:appId/floware/v1/voice-agents/${agentId}/tools`
);
return response;
}

/**
* Update a tool association for a voice agent
*/
async updateAgentTool(agentId: string, toolId: string, data: UpdateAgentToolRequest): Promise<ToolResponse> {
const response: IApiResponse<ToolData> = await this.http.patch(
`/v1/:appId/floware/v1/voice-agents/${agentId}/tools/${toolId}`,
data
);
return response;
}

/**
* Detach a tool from a voice agent
*/
async detachToolFromAgent(agentId: string, toolId: string): Promise<ToolResponse> {
const response: IApiResponse<ToolData> = await this.http.delete(
`/v1/:appId/floware/v1/voice-agents/${agentId}/tools/${toolId}`
);
return response;
}
}
29 changes: 29 additions & 0 deletions wavefront/client/src/components/ui/badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';

import { cn } from '@app/lib/utils';

const badgeVariants = cva(
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
{
variants: {
variant: {
default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
outline: 'text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
}
);

export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}

function Badge({ className, variant, ...props }: BadgeProps) {
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
}

export { Badge, badgeVariants };
65 changes: 65 additions & 0 deletions wavefront/client/src/constants/languages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Language constants for voice agent multi-language support
* Matches backend language validation
*/

export interface LanguageOption {
code: string;
name: string;
nativeName?: string;
}

export const SUPPORTED_LANGUAGES: LanguageOption[] = [
{ code: 'en', name: 'English' },
{ code: 'es', name: 'Spanish', nativeName: 'Español' },
{ code: 'hi', name: 'Hindi', nativeName: 'हिंदी' },
{ code: 'te', name: 'Telugu', nativeName: 'తెలుగు' },
{ code: 'ta', name: 'Tamil', nativeName: 'தமிழ்' },
{ code: 'kn', name: 'Kannada', nativeName: 'ಕನ್ನಡ' },
{ code: 'ml', name: 'Malayalam', nativeName: 'മലയാളം' },
{ code: 'mr', name: 'Marathi', nativeName: 'मराठी' },
{ code: 'gu', name: 'Gujarati', nativeName: 'ગુજરાતી' },
{ code: 'pa', name: 'Punjabi', nativeName: 'ਪੰਜਾਬੀ' },
{ code: 'bn', name: 'Bengali', nativeName: 'বাংলা' },
{ code: 'zh', name: 'Chinese', nativeName: '中文' },
{ code: 'ja', name: 'Japanese', nativeName: '日本語' },
{ code: 'ko', name: 'Korean', nativeName: '한국어' },
{ code: 'fr', name: 'French', nativeName: 'Français' },
{ code: 'de', name: 'German', nativeName: 'Deutsch' },
{ code: 'it', name: 'Italian', nativeName: 'Italiano' },
{ code: 'pt', name: 'Portuguese', nativeName: 'Português' },
{ code: 'ru', name: 'Russian', nativeName: 'Русский' },
{ code: 'ar', name: 'Arabic', nativeName: 'العربية' },
{ code: 'tr', name: 'Turkish', nativeName: 'Türkçe' },
{ code: 'vi', name: 'Vietnamese', nativeName: 'Tiếng Việt' },
{ code: 'th', name: 'Thai', nativeName: 'ภาษาไทย' },
{ code: 'id', name: 'Indonesian', nativeName: 'Bahasa Indonesia' },
{ code: 'ms', name: 'Malay', nativeName: 'Bahasa Melayu' },
{ code: 'nl', name: 'Dutch', nativeName: 'Nederlands' },
{ code: 'pl', name: 'Polish', nativeName: 'Polski' },
{ code: 'uk', name: 'Ukrainian', nativeName: 'Українська' },
{ code: 'ro', name: 'Romanian', nativeName: 'Română' },
{ code: 'cs', name: 'Czech', nativeName: 'Čeština' },
{ code: 'sv', name: 'Swedish', nativeName: 'Svenska' },
{ code: 'da', name: 'Danish', nativeName: 'Dansk' },
{ code: 'no', name: 'Norwegian', nativeName: 'Norsk' },
{ code: 'fi', name: 'Finnish', nativeName: 'Suomi' },
{ code: 'el', name: 'Greek', nativeName: 'Ελληνικά' },
{ code: 'he', name: 'Hebrew', nativeName: 'עברית' },
{ code: 'hu', name: 'Hungarian', nativeName: 'Magyar' },
{ code: 'sk', name: 'Slovak', nativeName: 'Slovenčina' },
{ code: 'bg', name: 'Bulgarian', nativeName: 'Български' },
{ code: 'hr', name: 'Croatian', nativeName: 'Hrvatski' },
{ code: 'fil', name: 'Filipino' },
];

export const getLanguageName = (code: string): string => {
const lang = SUPPORTED_LANGUAGES.find((l) => l.code === code);
return lang ? lang.name : code;
};

export const getLanguageDisplayName = (code: string): string => {
const lang = SUPPORTED_LANGUAGES.find((l) => l.code === code);
if (!lang) return code;
return lang.nativeName ? `${lang.name} (${lang.nativeName})` : lang.name;
};
39 changes: 36 additions & 3 deletions wavefront/client/src/hooks/data/fetch-hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { MessageProcessor, MessageProcessorListItem } from '@app/types/message-p
import { Pipeline, PipelineFile, PipelineStatus } from '@app/types/pipeline';
import { SttConfig } from '@app/types/stt-config';
import { TelephonyConfig } from '@app/types/telephony-config';
import { ToolDetails } from '@app/types/tool';
import { ToolDetails, VoiceAgentTool, VoiceAgentToolWithAssociation } from '@app/types/tool';
import { TtsConfig } from '@app/types/tts-config';
import { VoiceAgent } from '@app/types/voice-agent';
import { WorkflowListItem, WorkflowPipelineListItem, WorkflowRunListData } from '@app/types/workflow';
Expand Down Expand Up @@ -54,12 +54,15 @@ import {
getToolsQueryFn,
getTtsConfigQueryFn,
getTtsConfigsQueryFn,
getUsersQueryFn,
getVoiceAgentQueryFn,
getVoiceAgentToolQueryFn,
getVoiceAgentToolsQueryFn,
getAgentToolsQueryFn,
getVoiceAgentsQueryFn,
getWorkflowPipelinesQueryFn,
getWorkflowRunsQueryFn,
getWorkflowsQueryFn,
getUsersQueryFn,
readYamlQueryFn,
} from './query-functions';
import {
Expand Down Expand Up @@ -97,12 +100,15 @@ import {
getToolsKey,
getTtsConfigKey,
getTtsConfigsKey,
getUsersKey,
getVoiceAgentKey,
getVoiceAgentToolKey,
getVoiceAgentToolsKey,
getAgentToolsKey,
getVoiceAgentsKey,
getWorkflowPipelinesKey,
getWorkflowRunsKey,
getWorkflowsKey,
getUsersKey,
readYamlKey,
} from './query-keys';

Expand Down Expand Up @@ -414,3 +420,30 @@ export const useGetAppById = (appId: string, enabled: boolean = true): UseQueryR
export const useGetUsers = (): UseQueryResult<IUser[], Error> => {
return useQueryInit(getUsersKey(), getUsersQueryFn, true);
};

// Voice Agent Tools Hooks
export const useGetVoiceAgentTools = (appId: string | undefined): UseQueryResult<VoiceAgentTool[], Error> => {
return useQueryInit(getVoiceAgentToolsKey(appId || ''), getVoiceAgentToolsQueryFn, !!appId);
};

export const useGetVoiceAgentTool = (
appId: string | undefined,
toolId: string | undefined
): UseQueryResult<VoiceAgentTool | null, Error> => {
return useQueryInit(
getVoiceAgentToolKey(appId || '', toolId || ''),
() => getVoiceAgentToolQueryFn(toolId!),
!!appId && !!toolId
);
};

export const useGetAgentTools = (
appId: string | undefined,
agentId: string | undefined
): UseQueryResult<VoiceAgentToolWithAssociation[], Error> => {
return useQueryInit(
getAgentToolsKey(appId || '', agentId || ''),
() => getAgentToolsQueryFn(agentId!),
!!appId && !!agentId
);
};
32 changes: 30 additions & 2 deletions wavefront/client/src/hooks/data/query-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { MessageProcessor, MessageProcessorListItem } from '@app/types/message-p
import { Pipeline, PipelineFile, PipelineStatus } from '@app/types/pipeline';
import { SttConfig } from '@app/types/stt-config';
import { TelephonyConfig } from '@app/types/telephony-config';
import { ToolDetails } from '@app/types/tool';
import { ToolDetails, VoiceAgentTool, VoiceAgentToolWithAssociation } from '@app/types/tool';
import { TtsConfig } from '@app/types/tts-config';
import { IUser } from '@app/types/user';
import { VoiceAgent } from '@app/types/voice-agent';
Expand Down Expand Up @@ -371,6 +371,31 @@ const getAppByIdFn = async (appId: string) => {
return data?.app;
};

// Voice Agent Tools Query Functions
const getVoiceAgentToolsQueryFn = async (): Promise<VoiceAgentTool[]> => {
const response = await floConsoleService.toolService.listTools();
if (response.data?.meta?.status === 'success' && response.data.data?.tools) {
return response.data.data.tools;
}
return [];
};

const getVoiceAgentToolQueryFn = async (toolId: string): Promise<VoiceAgentTool | null> => {
const response = await floConsoleService.toolService.getTool(toolId);
if (response.data?.meta?.status === 'success' && response.data.data) {
return response.data.data;
}
return null;
};

const getAgentToolsQueryFn = async (agentId: string): Promise<VoiceAgentToolWithAssociation[]> => {
const response = await floConsoleService.toolService.getAgentTools(agentId);
if (response.data?.meta?.status === 'success' && response.data.data?.tools) {
return response.data.data.tools;
}
return [];
};

const getUsersQueryFn = async (): Promise<IUser[]> => {
const response = await floConsoleService.userService.listUsers();
if (response.data?.data?.users && Array.isArray(response.data.data.users)) {
Expand All @@ -382,6 +407,7 @@ const getUsersQueryFn = async (): Promise<IUser[]> => {
export {
getAgentQueryFn,
getAgentsQueryFn,
getAgentToolsQueryFn,
getAllAppsQueryFn,
getAllDatasourcesQueryFn,
getAllYamlsQueryFn,
Expand Down Expand Up @@ -414,11 +440,13 @@ export {
getToolsQueryFn,
getTtsConfigQueryFn,
getTtsConfigsQueryFn,
getUsersQueryFn,
getVoiceAgentQueryFn,
getVoiceAgentToolQueryFn,
getVoiceAgentToolsQueryFn,
getVoiceAgentsQueryFn,
getWorkflowPipelinesQueryFn,
getWorkflowRunsQueryFn,
getWorkflowsQueryFn,
getUsersQueryFn,
readYamlQueryFn,
};
10 changes: 8 additions & 2 deletions wavefront/client/src/hooks/data/query-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ const getPipelineFilesKey = (appId: string, pipelineId: string) => ['pipeline-fi
const getAppByIdKey = (appId: string) => ['app-by-id', appId];
const getUsersKey = () => ['users'];
const getUserKey = (userId: string) => ['user', userId];
const getVoiceAgentToolsKey = (appId: string) => ['voice-agent-tools', appId];
const getVoiceAgentToolKey = (appId: string, toolId: string) => ['voice-agent-tool', appId, toolId];
const getAgentToolsKey = (appId: string, agentId: string) => ['agent-tools', appId, agentId];

export {
getAgentKey,
getAgentsKey,
getAgentToolsKey,
getAllAppsKey,
getAllDatasourcesKey,
getApiServiceKey,
Expand Down Expand Up @@ -100,12 +104,14 @@ export {
getToolsKey,
getTtsConfigKey,
getTtsConfigsKey,
getUserKey,
getUsersKey,
getVoiceAgentKey,
getVoiceAgentToolKey,
getVoiceAgentToolsKey,
getVoiceAgentsKey,
getWorkflowPipelinesKey,
getWorkflowRunsKey,
getWorkflowsKey,
getAppByIdKey,
getUserKey,
getUsersKey,
};
Loading