Skip to content

Commit 73c757a

Browse files
brave search tool + fetch weather tool custom render; bug fixes
1 parent 927852a commit 73c757a

5 files changed

Lines changed: 124 additions & 1 deletion

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/************************************************************************
2+
* Copyright (C) 2025 Code Forge Temple *
3+
* This file is part of agentic-signal project *
4+
* See the LICENSE file in the project root for license details. *
5+
************************************************************************/
6+
7+
import {useGlobalConfig} from "../../../../../../stores/globalConfig";
8+
import {globalToolProp} from "../../utils/utils";
9+
import {DebouncedTextField} from "../../../../../DebouncedTextField";
10+
import {UserConfigFields} from "../../../UserConfigFields";
11+
import {excludeKeysFromObject} from "../../../../../../utils";
12+
13+
type BraveSearchToolConfigFieldsProps = {
14+
userConfig: Record<string, any>;
15+
onConfigChange: (key: string, value: string | number | boolean) => void;
16+
};
17+
18+
export function BraveSearchToolConfigFields ({userConfig, onConfigChange}: BraveSearchToolConfigFieldsProps) {
19+
const {globalData, setGlobalData} = useGlobalConfig();
20+
const globalApiKeyKey = globalToolProp("brave-search", "apiKey");
21+
const apiKeyValue = globalData[globalApiKeyKey] || userConfig?.apiKey || "";
22+
23+
return (
24+
<>
25+
<DebouncedTextField
26+
label="Brave Search API Key"
27+
value={apiKeyValue}
28+
onChange={(value) => {
29+
onConfigChange("apiKey", value);
30+
setGlobalData(globalApiKeyKey, value, true);
31+
}}
32+
fullWidth
33+
sx={{mb: 1}}
34+
type="password"
35+
helperText="Your Brave Search API key is retained globally for this tool."
36+
/>
37+
<UserConfigFields
38+
userConfigSchema={excludeKeysFromObject({
39+
apiKey: {type: "string", description: "Brave Search API Key", required: true},
40+
}, ["apiKey"])}
41+
userConfig={userConfig}
42+
onConfigChange={onConfigChange}
43+
/>
44+
</>
45+
);
46+
}

client/src/components/nodes/ToolNode/tools/BraveSearchTool/descriptor.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {GraphQLService} from "./services/graphqlService";
99
import {ToolDefinition} from "../types";
1010
import Brave from "./assets/brave.svg";
1111
import {extendSystemUserConfigSchema} from "../../../../../types/ollama.types";
12-
12+
import {BraveSearchToolConfigFields} from "./components/BraveSearchToolConfigFields";
1313

1414
export const BraveSearchToolDescriptor:ToolDefinition = {
1515
toolSubtype: "brave-search",
@@ -37,6 +37,14 @@ export const BraveSearchToolDescriptor:ToolDefinition = {
3737
}
3838
}),
3939
toSanitize: ["userConfig.apiKey"],
40+
renderConfig: function ({userConfig, onConfigChange}) {
41+
return (
42+
<BraveSearchToolConfigFields
43+
userConfig={userConfig}
44+
onConfigChange={onConfigChange}
45+
/>
46+
);
47+
},
4048
handlerFactory: (userConfig: { apiKey?: string, maxResults?: number }) => async ({query}: { query: string }) => {
4149
if (!userConfig.apiKey) {
4250
return {error: "API key must be specified. Please set apiKey in the configuration."};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/************************************************************************
2+
* Copyright (C) 2025 Code Forge Temple *
3+
* This file is part of agentic-signal project *
4+
* See the LICENSE file in the project root for license details. *
5+
************************************************************************/
6+
7+
import {useGlobalConfig} from "../../../../../../stores/globalConfig";
8+
import {globalToolProp} from "../../utils/utils";
9+
import {DebouncedTextField} from "../../../../../DebouncedTextField";
10+
11+
type FetchWeatherDataToolConfigFieldsProps = {
12+
userConfig: Record<string, any>;
13+
onConfigChange: (key: string, value: string | number | boolean) => void;
14+
};
15+
16+
export function FetchWeatherDataToolConfigFields ({userConfig, onConfigChange}: FetchWeatherDataToolConfigFieldsProps) {
17+
const {globalData, setGlobalData} = useGlobalConfig();
18+
const globalApiKeyKey = globalToolProp("fetch-weather-data", "apiKey");
19+
const apiKeyValue = globalData[globalApiKeyKey] || userConfig?.apiKey || "";
20+
21+
return (
22+
<DebouncedTextField
23+
label="WeatherAPI.com API Key"
24+
value={apiKeyValue}
25+
onChange={(value) => {
26+
onConfigChange("apiKey", value);
27+
setGlobalData(globalApiKeyKey, value, true);
28+
}}
29+
fullWidth
30+
sx={{mb: 1}}
31+
type="password"
32+
helperText="Your Weather API key is retained globally for this tool."
33+
/>
34+
);
35+
}

client/src/components/nodes/ToolNode/tools/FetchWeatherDataTool/descriptor.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {Thunderstorm} from "iconoir-react";
88
import {ToolDefinition} from "../types";
99
import {extendSystemUserConfigSchema} from "../../../../../types/ollama.types";
1010
import {sanitizeStringInput} from "../utils/sanitize";
11+
import {FetchWeatherDataToolConfigFields} from "./components/FetchWeatherDataToolConfigFields";
1112

1213

1314
export const FetchWeatherDataToolDescriptor:ToolDefinition = {
@@ -37,6 +38,14 @@ export const FetchWeatherDataToolDescriptor:ToolDefinition = {
3738
apiKey: {type: "string", description: "WeatherAPI.com API Key", required: true}
3839
}),
3940
toSanitize: ["userConfig.apiKey"],
41+
renderConfig: function ({userConfig, onConfigChange}) {
42+
return (
43+
<FetchWeatherDataToolConfigFields
44+
userConfig={userConfig}
45+
onConfigChange={onConfigChange}
46+
/>
47+
);
48+
},
4049
handlerFactory: (userConfig: { apiKey?: string }) => async ({city, date}: { city: string, date?: string }) => {
4150
if (!userConfig.apiKey) {
4251
return {error: "API key must be specified. Please set apiKey in the configuration."};

server/ws/webSocketManager.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export class WebSocketManager {
1717

1818
private subscriptionFactories = new Map<string, SubscriptionFactory>();
1919

20+
private pingIntervals = new Map<WebSocket, ReturnType<typeof setInterval>>();
21+
2022
private yogaHandler: ((request: Request) => Promise<Response>) | null = null;
2123

2224
private socketSubscriptions = new Map<WebSocket, Map<string, string>>();
@@ -83,6 +85,17 @@ export class WebSocketManager {
8385
private setupSocketHandlers (socket: WebSocket): void {
8486
this.socketSubscriptions.set(socket, new Map());
8587

88+
const pingInterval = setInterval(() => {
89+
if (socket.readyState === WebSocket.OPEN) {
90+
socket.send(JSON.stringify({type: 'ping'}));
91+
} else {
92+
clearInterval(pingInterval);
93+
this.pingIntervals.delete(socket);
94+
}
95+
}, 30_000);
96+
97+
this.pingIntervals.set(socket, pingInterval);
98+
8699
socket.onopen = () => { };
87100

88101
socket.onmessage = async (event) => {
@@ -126,6 +139,7 @@ export class WebSocketManager {
126139

127140
socket.onerror = (error) => {
128141
console.error('[WebSocketManager] ❌ Socket error:', error);
142+
this.clearPingInterval(socket);
129143
};
130144
}
131145

@@ -212,7 +226,18 @@ export class WebSocketManager {
212226
socket.addEventListener('close', closeHandler, {once: true});
213227
}
214228

229+
private clearPingInterval (socket: WebSocket): void {
230+
const interval = this.pingIntervals.get(socket);
231+
232+
if (interval !== undefined) {
233+
clearInterval(interval);
234+
this.pingIntervals.delete(socket);
235+
}
236+
}
237+
215238
private cleanupSocket (socket: WebSocket): void {
239+
this.clearPingInterval(socket);
240+
216241
for (const [key, sockets] of this.activeSubscriptions.entries()) {
217242
sockets.delete(socket);
218243

0 commit comments

Comments
 (0)