Skip to content

Commit abd883a

Browse files
committed
Scripting command engine
1 parent 74728e1 commit abd883a

File tree

1 file changed

+211
-0
lines changed
  • gai-frontend/lib/chat/scripting/extensions

1 file changed

+211
-0
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/// Command Layer Implementation
2+
/// <reference path="../chat_scripting_api.ts" />
3+
4+
// Types
5+
interface CommandContext {
6+
chatHistory: ReadonlyArray<ChatMessage>;
7+
selectedModels: ReadonlyArray<ModelInfo>;
8+
scriptState: Record<string, any>;
9+
}
10+
11+
interface CommandDefinition {
12+
pattern: string;
13+
handler: (input: CommandInput, context: CommandContext) => Promise<void>;
14+
description: string;
15+
priority?: number;
16+
}
17+
18+
interface CommandInput {
19+
raw: string;
20+
command: string;
21+
args: string[];
22+
groups: Record<string, string>;
23+
}
24+
25+
// Global command processor instance
26+
const commandProcessor = new class {
27+
private commands: CommandDefinition[] = [];
28+
private fallbackHandler: ((input: string, context: CommandContext) => Promise<void>) | null = null;
29+
private scriptState: Record<string, any> = {};
30+
31+
register(command: CommandDefinition) {
32+
const priority = command.priority || 0;
33+
const index = this.commands.findIndex(cmd => (cmd.priority || 0) < priority);
34+
if (index === -1) {
35+
this.commands.push(command);
36+
} else {
37+
this.commands.splice(index, 0, command);
38+
}
39+
}
40+
41+
unregister(pattern: string) {
42+
this.commands = this.commands.filter(cmd => cmd.pattern !== pattern);
43+
}
44+
45+
setFallback(handler: (input: string, context: CommandContext) => Promise<void>) {
46+
this.fallbackHandler = handler;
47+
}
48+
49+
private parseCommand(input: string): CommandInput | null {
50+
for (const command of this.commands) {
51+
if (command.pattern.startsWith('/')) {
52+
const parts = input.split(' ');
53+
if (parts[0] === command.pattern) {
54+
return {
55+
raw: input,
56+
command: parts[0],
57+
args: parts.slice(1),
58+
groups: {}
59+
};
60+
}
61+
} else {
62+
try {
63+
const regex = new RegExp(command.pattern);
64+
const match = input.match(regex);
65+
if (match) {
66+
return {
67+
raw: input,
68+
command: match[0],
69+
args: match.slice(1),
70+
groups: match.groups || {}
71+
};
72+
}
73+
} catch (e) {
74+
console.error(`Invalid regex pattern: ${command.pattern}`);
75+
}
76+
}
77+
}
78+
return null;
79+
}
80+
81+
async process(input: string): Promise<void> {
82+
const context: CommandContext = {
83+
chatHistory: getChatHistory(),
84+
selectedModels: getUserSelectedModels(),
85+
scriptState: this.scriptState
86+
};
87+
88+
const parsed = this.parseCommand(input);
89+
90+
if (parsed) {
91+
const command = this.commands.find(cmd =>
92+
cmd.pattern.startsWith('/') ? cmd.pattern === parsed.command :
93+
input.match(new RegExp(cmd.pattern))
94+
);
95+
96+
if (command) {
97+
try {
98+
await command.handler(parsed, context);
99+
} catch (error) {
100+
const errorMsg = `Command execution failed: ${error.message}`;
101+
chatSystemMessage(errorMsg);
102+
throw new Error(errorMsg);
103+
}
104+
return;
105+
}
106+
}
107+
108+
if (this.fallbackHandler) {
109+
await this.fallbackHandler(input, context);
110+
} else {
111+
throw new Error('No matching command or fallback handler');
112+
}
113+
}
114+
115+
getHelp(): string {
116+
return this.commands
117+
.map(cmd => `${cmd.pattern}: ${cmd.description}`)
118+
.join('\n');
119+
}
120+
}();
121+
122+
// Register built-in commands
123+
commandProcessor.register({
124+
pattern: '/help',
125+
handler: async () => {
126+
chatSystemMessage(commandProcessor.getHelp());
127+
},
128+
description: 'Show available commands'
129+
});
130+
131+
// Register example commands
132+
commandProcessor.register({
133+
pattern: '/party',
134+
description: 'Send message to all selected models',
135+
handler: async (input: CommandInput) => {
136+
chatSystemMessage('Party mode activated!');
137+
const userPrompt = input.args.join(' ');
138+
chatClientMessage(userPrompt);
139+
140+
for (const model of getUserSelectedModels()) {
141+
const filteredMessages = getConversation();
142+
const response = await chatSendToModel(filteredMessages, model.id);
143+
addChatMessage(response);
144+
}
145+
}
146+
});
147+
148+
commandProcessor.register({
149+
pattern: '/improve',
150+
description: 'Improve prompt using Claude before sending',
151+
handler: async (input: CommandInput) => {
152+
const userPrompt = input.args.join(' ');
153+
chatSystemMessage('Improving prompt...');
154+
155+
const improvementMessage = new ChatMessage(
156+
ChatMessageSource.CLIENT,
157+
`You are a prompt improvement assistant. Your task is to improve the following prompt to be ` +
158+
`more specific, clear, and effective. Maintain the original intent but make it more detailed ` +
159+
`and precise. Respond with only the improved prompt, no explanations: ` +
160+
`{BEGIN_PROMPT}${userPrompt}{END_PROMPT}`,
161+
{}
162+
);
163+
164+
const improvedResponse = await chatSendToModel([improvementMessage], 'claude-3-5-sonnet-latest');
165+
const improvedPrompt = improvedResponse.msg.trim();
166+
167+
chatSystemMessage(`Original prompt: ${userPrompt}`);
168+
chatInternalMessage(`Improved prompt: ${improvedPrompt}`);
169+
170+
const userMessage = new ChatMessage(ChatMessageSource.CLIENT, improvedPrompt, {});
171+
addChatMessage(userMessage);
172+
173+
const selectedModels = getUserSelectedModels();
174+
if (selectedModels.length > 0) {
175+
const modelId = selectedModels[0].id;
176+
addChatMessage(await sendMessagesToModel([userMessage], modelId, null));
177+
}
178+
}
179+
});
180+
181+
// Set default fallback
182+
commandProcessor.setFallback(async (input: string) => {
183+
const selectedModels = getUserSelectedModels();
184+
if (selectedModels.length === 0) {
185+
chatSystemMessage('No models selected');
186+
return;
187+
}
188+
189+
const userMessage = new ChatMessage(ChatMessageSource.CLIENT, input, {});
190+
addChatMessage(userMessage);
191+
192+
const response = await chatSendToModel([userMessage], selectedModels[0].id);
193+
if (response) {
194+
addChatMessage(response);
195+
}
196+
});
197+
198+
// Entry point implementation
199+
function onUserPrompt(userPrompt: string): void {
200+
(async () => {
201+
try {
202+
await commandProcessor.process(userPrompt);
203+
} catch (error) {
204+
console.error('Error processing command:', error);
205+
chatSystemMessage(error.message);
206+
}
207+
})();
208+
}
209+
210+
// Signal that the script is loaded
211+
chatSystemMessage('Command processor initialized');

0 commit comments

Comments
 (0)