Skip to content

Commit 5284aa8

Browse files
small fix for mentions to hide endpoints
1 parent 19bc7d3 commit 5284aa8

21 files changed

Lines changed: 1027 additions & 7 deletions
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
#!/usr/bin/env node
2+
3+
4+
// Basic usage:
5+
// cd api && node test/create-agent-from-prompt.js burn_monitor.txt
6+
7+
// With custom user ID:
8+
// cd api && node test/create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834
9+
10+
// With options:
11+
// cd api && node test/create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834
12+
// '{"tools":["web_search","workflows"],"provider":"anthropic","model":"claude-3-sonnet"}'
13+
14+
const path = require('path');
15+
16+
// Disable MeiliSearch BEFORE loading dotenv to avoid connection errors in script
17+
process.env.SEARCH = 'false';
18+
process.env.MEILI_NO_SYNC = 'true';
19+
delete process.env.MEILI_HOST;
20+
delete process.env.MEILI_MASTER_KEY;
21+
22+
require('dotenv').config({ path: path.join(__dirname, '../../.env') });
23+
24+
// Re-disable after dotenv loads
25+
process.env.SEARCH = 'false';
26+
process.env.MEILI_NO_SYNC = 'true';
27+
delete process.env.MEILI_HOST;
28+
delete process.env.MEILI_MASTER_KEY;
29+
30+
const fs = require('fs');
31+
const { nanoid } = require('nanoid');
32+
33+
const { connectDb } = require('../db/connect');
34+
const { Agent } = require('../db/models');
35+
36+
/**
37+
* Parse agent prompt file to extract Name, Description, Instructions, and Default prompts
38+
* @param {string} filePath - Path to the prompt file
39+
* @returns {Object} Parsed agent data
40+
*/
41+
function parsePromptFile(filePath) {
42+
if (!fs.existsSync(filePath)) {
43+
throw new Error(`File not found: ${filePath}`);
44+
}
45+
46+
const content = fs.readFileSync(filePath, 'utf8');
47+
const lines = content.split('\n');
48+
49+
let name = '';
50+
let description = '';
51+
let instructions = '';
52+
let defaultPrompts = [];
53+
let currentSection = '';
54+
55+
for (let i = 0; i < lines.length; i++) {
56+
const line = lines[i].trim();
57+
58+
if (line.startsWith('Name:')) {
59+
currentSection = 'name';
60+
name = line.replace('Name:', '').trim();
61+
continue;
62+
}
63+
64+
if (line.startsWith('Description:')) {
65+
currentSection = 'description';
66+
description = line.replace('Description:', '').trim();
67+
continue;
68+
}
69+
70+
if (line.startsWith('Instructions:')) {
71+
currentSection = 'instructions';
72+
instructions = line.replace('Instructions:', '').trim();
73+
continue;
74+
}
75+
76+
if (line.startsWith('Default prompts:')) {
77+
currentSection = 'default_prompts';
78+
continue;
79+
}
80+
81+
// Continue reading content for current section
82+
if (currentSection === 'name' && line && !line.startsWith('Description:') && !line.startsWith('Instructions:') && !line.startsWith('Default prompts:')) {
83+
name += (name ? ' ' : '') + line;
84+
} else if (currentSection === 'description' && line && !line.startsWith('Instructions:') && !line.startsWith('Default prompts:')) {
85+
description += (description ? ' ' : '') + line;
86+
} else if (currentSection === 'instructions' && line && !line.startsWith('Default prompts:')) {
87+
instructions += (instructions ? '\n' : '') + lines[i]; // Keep original formatting for instructions
88+
} else if (currentSection === 'default_prompts' && line) {
89+
// Parse numbered list items or quoted strings
90+
const promptMatch = line.match(/^\d+\.\s*["'](.+)["']\s*$/) || line.match(/^\d+\.\s*(.+)$/);
91+
if (promptMatch) {
92+
const prompt = promptMatch[1].trim();
93+
if (prompt) {
94+
defaultPrompts.push(prompt);
95+
}
96+
} else if (line.startsWith('"') && line.endsWith('"')) {
97+
// Handle quoted strings without numbers
98+
defaultPrompts.push(line.slice(1, -1));
99+
} else if (line && !line.match(/^\d+\.\s*$/)) {
100+
// Handle unquoted strings that aren't just numbers
101+
defaultPrompts.push(line);
102+
}
103+
}
104+
}
105+
106+
return {
107+
name: name.trim(),
108+
description: description.trim(),
109+
instructions: instructions.trim(),
110+
defaultPrompts: defaultPrompts
111+
};
112+
}
113+
114+
/**
115+
* Create an agent with the provided data.
116+
*/
117+
async function createAgent(agentData) {
118+
const { author, ...versionData } = agentData;
119+
const timestamp = new Date();
120+
const initialAgentData = {
121+
...agentData,
122+
versions: [
123+
{
124+
...versionData,
125+
createdAt: timestamp,
126+
updatedAt: timestamp,
127+
},
128+
],
129+
};
130+
return (await Agent.create(initialAgentData)).toObject();
131+
}
132+
133+
/**
134+
* Update an existing agent with a new version
135+
*/
136+
async function updateAgentVersion(existingAgent, newAgentData) {
137+
const { author, ...versionData } = newAgentData;
138+
const timestamp = new Date();
139+
140+
// Create new version object
141+
const newVersion = {
142+
...versionData,
143+
createdAt: timestamp,
144+
updatedAt: timestamp,
145+
};
146+
147+
// Update the main agent fields with new data
148+
const updatedAgent = await Agent.findByIdAndUpdate(
149+
existingAgent._id,
150+
{
151+
...newAgentData,
152+
$push: { versions: newVersion },
153+
updatedAt: timestamp,
154+
},
155+
{ new: true }
156+
);
157+
158+
return updatedAgent.toObject();
159+
}
160+
161+
/**
162+
* Create agent in database from prompt file
163+
* @param {string} filename - Name of the prompt file
164+
* @param {string} userId - MongoDB ObjectId of the user (optional, defaults to 6831a77a46d7304e714d8248)
165+
* @param {Object} options - Additional options
166+
*/
167+
async function createAgentFromPrompt(filename, userId = '6831a77a46d7304e714d8248', options = {}) {
168+
try {
169+
// Connect to database
170+
await connectDb();
171+
172+
// Build file path
173+
const promptsPath = path.join(__dirname, '../../user_agent_system_prompts');
174+
const filePath = path.join(promptsPath, filename);
175+
176+
// Parse prompt file
177+
const { name, description, instructions, defaultPrompts } = parsePromptFile(filePath);
178+
179+
if (!name || !description || !instructions) {
180+
throw new Error('Missing required fields: Name, Description, or Instructions');
181+
}
182+
183+
// Check if an agent with the same name and author already exists
184+
const existingAgent = await Agent.findOne({ name, author: userId });
185+
186+
let agent;
187+
let isNewAgent = false;
188+
189+
if (existingAgent) {
190+
console.log(`🔄 Found existing agent "${name}" - creating new version...`);
191+
192+
// Prepare agent data for new version (keep existing ID)
193+
const agentData = {
194+
id: existingAgent.id, // Keep the same ID
195+
name,
196+
description,
197+
instructions,
198+
provider: options.provider || 'openAI',
199+
model: options.model || 'gpt-4.1-mini',
200+
artifacts: options.artifacts || 'default',
201+
author: userId,
202+
tools: options.tools || ['workflows'],
203+
tool_kwargs: options.tool_kwargs || [],
204+
agent_ids: options.agent_ids || [],
205+
conversation_starters: options.conversation_starters || [],
206+
default_prompts: options.default_prompts || defaultPrompts || [],
207+
projectIds: options.projectIds || [],
208+
mcp_servers: options.mcp_servers || [],
209+
model_parameters: options.model_parameters || {},
210+
end_after_tools: options.end_after_tools || false,
211+
hide_sequential_outputs: options.hide_sequential_outputs || false
212+
};
213+
214+
// Update existing agent with new version
215+
agent = await updateAgentVersion(existingAgent, agentData);
216+
217+
} else {
218+
console.log(`🆕 Creating new agent "${name}"...`);
219+
isNewAgent = true;
220+
221+
// Generate unique agent ID for new agent
222+
const agentId = `agent_${nanoid()}`;
223+
224+
// Prepare agent data with defaults
225+
const agentData = {
226+
id: agentId,
227+
name,
228+
description,
229+
instructions,
230+
provider: options.provider || 'openAI',
231+
model: options.model || 'gpt-4.1-mini',
232+
artifacts: options.artifacts || 'default',
233+
author: userId,
234+
tools: options.tools || ['workflows'],
235+
tool_kwargs: options.tool_kwargs || [],
236+
agent_ids: options.agent_ids || [],
237+
conversation_starters: options.conversation_starters || [],
238+
default_prompts: options.default_prompts || defaultPrompts || [],
239+
projectIds: options.projectIds || [],
240+
mcp_servers: options.mcp_servers || [],
241+
model_parameters: options.model_parameters || {},
242+
end_after_tools: options.end_after_tools || false,
243+
hide_sequential_outputs: options.hide_sequential_outputs || false
244+
};
245+
246+
// Create new agent in database
247+
agent = await createAgent(agentData);
248+
}
249+
250+
console.log(`✅ Agent ${isNewAgent ? 'created' : 'updated'} successfully!`);
251+
console.log(`📄 Name: ${agent.name}`);
252+
console.log(`🆔 ID: ${agent.id}`);
253+
console.log(`👤 Author: ${agent.author}`);
254+
console.log(`📝 Description: ${agent.description}`);
255+
console.log(`📊 Versions: ${agent.versions ? agent.versions.length : 1}`);
256+
257+
return agent;
258+
259+
} catch (error) {
260+
console.error('❌ Error creating agent:', error.message);
261+
throw error;
262+
}
263+
}
264+
265+
// CLI interface
266+
if (require.main === module) {
267+
const args = process.argv.slice(2);
268+
269+
if (args.length < 1) {
270+
console.log('Usage: node create-agent-from-prompt.js <filename> [user_id] [options]');
271+
console.log('');
272+
console.log('Arguments:');
273+
console.log(' filename - Name of the prompt file (e.g., burn_monitor.txt)');
274+
console.log(' user_id - MongoDB ObjectId of the user (optional, defaults to 6831a77a46d7304e714d8248)');
275+
console.log('');
276+
console.log('Options (JSON format):');
277+
console.log(' --provider - AI provider (default: "openAI")');
278+
console.log(' --model - Model name (default: "gpt-4.1-mini")');
279+
console.log(' --tools - Array of tool names');
280+
console.log(' --mcp_servers - Array of MCP server names');
281+
console.log('');
282+
console.log('Examples:');
283+
console.log(' node create-agent-from-prompt.js burn_monitor.txt');
284+
console.log(' node create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834');
285+
console.log(' node create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834 \'{"tools":["web_search"],"provider":"anthropic","model":"claude-3-sonnet"}\'');
286+
process.exit(1);
287+
}
288+
289+
const filename = args[0];
290+
const userId = args[1] || '6831a77a46d7304e714d8248'; // Default user ID
291+
const optionsJson = args[2] || '{}';
292+
293+
let options = {};
294+
try {
295+
options = JSON.parse(optionsJson);
296+
} catch (error) {
297+
console.error('❌ Invalid JSON in options:', error.message);
298+
process.exit(1);
299+
}
300+
301+
createAgentFromPrompt(filename, userId, options)
302+
.then(() => {
303+
console.log('🎉 Done!');
304+
process.exit(0);
305+
})
306+
.catch((error) => {
307+
console.error('💥 Failed:', error.message);
308+
process.exit(1);
309+
});
310+
}
311+
312+
module.exports = { createAgentFromPrompt, parsePromptFile };

client/src/components/Chat/Header.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,25 @@ export default function Header() {
5656
endpointsConfig,
5757
});
5858

59-
// Check if only agents are available
59+
// Check if only agents are available or if modelSelect is disabled but agents are enabled
6060
const onlyAgentsAvailable = useMemo(() => {
61-
if (!interfaceConfig.modelSelect || !hasAgentAccess) {
61+
if (!hasAgentAccess) {
6262
return false;
6363
}
6464

65-
const availableEndpoints = mappedEndpoints.filter(endpoint => endpoint.hasModels);
66-
return availableEndpoints.length === 1 && availableEndpoints[0]?.value === EModelEndpoint.agents;
67-
}, [interfaceConfig.modelSelect, hasAgentAccess, mappedEndpoints]);
65+
// If modelSelect is disabled but agents are enabled, show agent selector
66+
if (!interfaceConfig.modelSelect && interfaceConfig.agents) {
67+
return true;
68+
}
69+
70+
// If modelSelect is enabled, check if only agents endpoint is available
71+
if (interfaceConfig.modelSelect) {
72+
const availableEndpoints = mappedEndpoints.filter(endpoint => endpoint.hasModels);
73+
return availableEndpoints.length === 1 && availableEndpoints[0]?.value === EModelEndpoint.agents;
74+
}
75+
76+
return false;
77+
}, [interfaceConfig.modelSelect, interfaceConfig.agents, hasAgentAccess, mappedEndpoints]);
6878

6979
const isSmallScreen = useMediaQuery('(max-width: 768px)');
7080

client/src/hooks/Input/useMentions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export default function useMentions({
157157
});
158158

159159
const mentions = [
160-
...(modelSpecs.length > 0 ? modelSpecs : []).map((modelSpec) => ({
160+
...(interfaceConfig.modelSelect === true && modelSpecs.length > 0 ? modelSpecs : []).map((modelSpec) => ({
161161
value: modelSpec.name,
162162
label: modelSpec.label,
163163
description: modelSpec.description,
@@ -183,7 +183,7 @@ export default function useMentions({
183183
size: 20,
184184
}),
185185
})),
186-
...(interfaceConfig.modelSelect === true ? (agentsList ?? []) : []),
186+
...(interfaceConfig.agents === true ? (agentsList ?? []) : []),
187187
...(endpointsConfig?.[EModelEndpoint.assistants] &&
188188
includeAssistants &&
189189
interfaceConfig.modelSelect === true

0 commit comments

Comments
 (0)