mirror of
https://github.com/github/awesome-copilot.git
synced 2026-02-20 02:15:12 +00:00
Address review feedback: fix event handler leak, error handling, model alignment
- Move session.On handler outside loop to prevent handler accumulation (C#) - Use TrySetResult instead of SetResult to avoid duplicate-set exceptions (C#) - Wrap CreateSessionAsync in broader try/finally so client always stops (C#) - Fix PersistentRalphLoop to use maxIterations parameter instead of hardcoded 10 - Align model name to gpt-5.1-codex-mini across all doc snippets - Fix completion promise DONE -> COMPLETE in usage snippet - Replace Claude references with generic model terminology
This commit is contained in:
@@ -20,13 +20,13 @@ RALPH-loop is a development methodology for iterative AI-powered task completion
|
||||
|
||||
## Example Scenario
|
||||
|
||||
You need to iteratively improve code until all tests pass. Instead of asking Claude to "write perfect code," you use RALPH-loop to:
|
||||
You need to iteratively improve code until all tests pass. Instead of asking the model to "write perfect code," you use RALPH-loop to:
|
||||
|
||||
1. Send the initial prompt with clear success criteria
|
||||
2. Claude writes code and tests
|
||||
3. Claude runs tests and sees failures
|
||||
2. The model writes code and tests
|
||||
3. The model runs tests and sees failures
|
||||
4. Loop automatically re-sends the prompt
|
||||
5. Claude reads test output and previous code, fixes issues
|
||||
5. The model reads test output and previous code, fixes issues
|
||||
6. Repeat until all tests pass and completion promise is output
|
||||
|
||||
## Basic Implementation
|
||||
@@ -52,56 +52,66 @@ public class RalphLoop
|
||||
public async Task<string> RunAsync(string prompt)
|
||||
{
|
||||
await _client.StartAsync();
|
||||
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
|
||||
|
||||
try
|
||||
{
|
||||
while (_iteration < _maxIterations)
|
||||
{
|
||||
_iteration++;
|
||||
Console.WriteLine($"\n--- Iteration {_iteration} ---");
|
||||
var session = await _client.CreateSessionAsync(
|
||||
new SessionConfig { Model = "gpt-5.1-codex-mini" });
|
||||
|
||||
try
|
||||
{
|
||||
var done = new TaskCompletionSource<string>();
|
||||
session.On(evt =>
|
||||
{
|
||||
if (evt is AssistantMessageEvent msg)
|
||||
{
|
||||
_lastResponse = msg.Data.Content;
|
||||
done.SetResult(msg.Data.Content);
|
||||
done.TrySetResult(msg.Data.Content);
|
||||
}
|
||||
});
|
||||
|
||||
// Send prompt (on first iteration) or continuation
|
||||
var messagePrompt = _iteration == 1
|
||||
? prompt
|
||||
: $"{prompt}\n\nPrevious attempt:\n{_lastResponse}\n\nContinue iterating...";
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = messagePrompt });
|
||||
var response = await done.Task;
|
||||
|
||||
// Check for completion promise
|
||||
if (response.Contains(_completionPromise))
|
||||
while (_iteration < _maxIterations)
|
||||
{
|
||||
Console.WriteLine($"✓ Completion promise detected: {_completionPromise}");
|
||||
return response;
|
||||
_iteration++;
|
||||
Console.WriteLine($"\n--- Iteration {_iteration} ---");
|
||||
|
||||
done = new TaskCompletionSource<string>();
|
||||
|
||||
// Send prompt (on first iteration) or continuation
|
||||
var messagePrompt = _iteration == 1
|
||||
? prompt
|
||||
: $"{prompt}\n\nPrevious attempt:\n{_lastResponse}\n\nContinue iterating...";
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = messagePrompt });
|
||||
var response = await done.Task;
|
||||
|
||||
// Check for completion promise
|
||||
if (response.Contains(_completionPromise))
|
||||
{
|
||||
Console.WriteLine($"✓ Completion promise detected: {_completionPromise}");
|
||||
return response;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
|
||||
}
|
||||
|
||||
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
|
||||
throw new InvalidOperationException(
|
||||
$"Max iterations ({_maxIterations}) reached without completion promise");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await session.DisposeAsync();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Max iterations ({_maxIterations}) reached without completion promise");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await session.DisposeAsync();
|
||||
await _client.StopAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
var loop = new RalphLoop(maxIterations: 5, completionPromise: "DONE");
|
||||
var loop = new RalphLoop(maxIterations: 5, completionPromise: "COMPLETE");
|
||||
var result = await loop.RunAsync("Your task here");
|
||||
Console.WriteLine(result);
|
||||
```
|
||||
@@ -115,11 +125,13 @@ public class PersistentRalphLoop
|
||||
{
|
||||
private readonly string _workDir;
|
||||
private readonly CopilotClient _client;
|
||||
private readonly int _maxIterations;
|
||||
private int _iteration = 0;
|
||||
|
||||
public PersistentRalphLoop(string workDir, int maxIterations = 10)
|
||||
{
|
||||
_workDir = workDir;
|
||||
_maxIterations = maxIterations;
|
||||
Directory.CreateDirectory(_workDir);
|
||||
_client = new CopilotClient();
|
||||
}
|
||||
@@ -127,26 +139,17 @@ public class PersistentRalphLoop
|
||||
public async Task<string> RunAsync(string prompt)
|
||||
{
|
||||
await _client.StartAsync();
|
||||
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
|
||||
|
||||
try
|
||||
{
|
||||
// Store initial prompt
|
||||
var promptFile = Path.Combine(_workDir, "prompt.md");
|
||||
await File.WriteAllTextAsync(promptFile, prompt);
|
||||
var session = await _client.CreateSessionAsync(
|
||||
new SessionConfig { Model = "gpt-5.1-codex-mini" });
|
||||
|
||||
while (_iteration < 10)
|
||||
try
|
||||
{
|
||||
_iteration++;
|
||||
Console.WriteLine($"\n--- Iteration {_iteration} ---");
|
||||
|
||||
// Build context including previous work
|
||||
var contextBuilder = new StringBuilder(prompt);
|
||||
var previousOutput = Path.Combine(_workDir, $"output-{_iteration - 1}.txt");
|
||||
if (File.Exists(previousOutput))
|
||||
{
|
||||
contextBuilder.AppendLine($"\nPrevious iteration output:\n{await File.ReadAllTextAsync(previousOutput)}");
|
||||
}
|
||||
// Store initial prompt
|
||||
var promptFile = Path.Combine(_workDir, "prompt.md");
|
||||
await File.WriteAllTextAsync(promptFile, prompt);
|
||||
|
||||
var done = new TaskCompletionSource<string>();
|
||||
string response = "";
|
||||
@@ -155,29 +158,48 @@ public class PersistentRalphLoop
|
||||
if (evt is AssistantMessageEvent msg)
|
||||
{
|
||||
response = msg.Data.Content;
|
||||
done.SetResult(msg.Data.Content);
|
||||
done.TrySetResult(msg.Data.Content);
|
||||
}
|
||||
});
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = contextBuilder.ToString() });
|
||||
await done.Task;
|
||||
|
||||
// Persist output
|
||||
await File.WriteAllTextAsync(
|
||||
Path.Combine(_workDir, $"output-{_iteration}.txt"),
|
||||
response);
|
||||
|
||||
if (response.Contains("COMPLETE"))
|
||||
while (_iteration < _maxIterations)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
}
|
||||
_iteration++;
|
||||
Console.WriteLine($"\n--- Iteration {_iteration} ---");
|
||||
|
||||
throw new InvalidOperationException("Max iterations reached");
|
||||
done = new TaskCompletionSource<string>();
|
||||
|
||||
// Build context including previous work
|
||||
var contextBuilder = new StringBuilder(prompt);
|
||||
var previousOutput = Path.Combine(_workDir, $"output-{_iteration - 1}.txt");
|
||||
if (File.Exists(previousOutput))
|
||||
{
|
||||
contextBuilder.AppendLine($"\nPrevious iteration output:\n{await File.ReadAllTextAsync(previousOutput)}");
|
||||
}
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = contextBuilder.ToString() });
|
||||
await done.Task;
|
||||
|
||||
// Persist output
|
||||
await File.WriteAllTextAsync(
|
||||
Path.Combine(_workDir, $"output-{_iteration}.txt"),
|
||||
response);
|
||||
|
||||
if (response.Contains("COMPLETE"))
|
||||
{
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Max iterations reached");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await session.DisposeAsync();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
await session.DisposeAsync();
|
||||
await _client.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,54 +62,63 @@ public class RalphLoop
|
||||
public async Task<string> RunAsync(string initialPrompt)
|
||||
{
|
||||
await _client.StartAsync();
|
||||
var session = await _client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5.1-codex-mini"
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
while (_iteration < _maxIterations)
|
||||
{
|
||||
_iteration++;
|
||||
Console.WriteLine($"\n=== Iteration {_iteration}/{_maxIterations} ===");
|
||||
var session = await _client.CreateSessionAsync(new SessionConfig
|
||||
{
|
||||
Model = "gpt-5.1-codex-mini"
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
var done = new TaskCompletionSource<string>();
|
||||
session.On(evt =>
|
||||
{
|
||||
if (evt is AssistantMessageEvent msg)
|
||||
{
|
||||
_lastResponse = msg.Data.Content;
|
||||
done.SetResult(msg.Data.Content);
|
||||
done.TrySetResult(msg.Data.Content);
|
||||
}
|
||||
});
|
||||
|
||||
var currentPrompt = BuildIterationPrompt(initialPrompt);
|
||||
Console.WriteLine($"Sending prompt (length: {currentPrompt.Length})...");
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = currentPrompt });
|
||||
var response = await done.Task;
|
||||
|
||||
var summary = response.Length > 200
|
||||
? response.Substring(0, 200) + "..."
|
||||
: response;
|
||||
Console.WriteLine($"Response: {summary}");
|
||||
|
||||
if (response.Contains(_completionPromise))
|
||||
while (_iteration < _maxIterations)
|
||||
{
|
||||
Console.WriteLine($"\n✓ Completion promise detected: '{_completionPromise}'");
|
||||
return response;
|
||||
_iteration++;
|
||||
Console.WriteLine($"\n=== Iteration {_iteration}/{_maxIterations} ===");
|
||||
|
||||
done = new TaskCompletionSource<string>();
|
||||
|
||||
var currentPrompt = BuildIterationPrompt(initialPrompt);
|
||||
Console.WriteLine($"Sending prompt (length: {currentPrompt.Length})...");
|
||||
|
||||
await session.SendAsync(new MessageOptions { Prompt = currentPrompt });
|
||||
var response = await done.Task;
|
||||
|
||||
var summary = response.Length > 200
|
||||
? response.Substring(0, 200) + "..."
|
||||
: response;
|
||||
Console.WriteLine($"Response: {summary}");
|
||||
|
||||
if (response.Contains(_completionPromise))
|
||||
{
|
||||
Console.WriteLine($"\n✓ Completion promise detected: '{_completionPromise}'");
|
||||
return response;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
|
||||
}
|
||||
|
||||
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
|
||||
throw new InvalidOperationException(
|
||||
$"Max iterations ({_maxIterations}) reached without completion promise: '{_completionPromise}'");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await session.DisposeAsync();
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Max iterations ({_maxIterations}) reached without completion promise: '{_completionPromise}'");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await session.DisposeAsync();
|
||||
await _client.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class RalphLoop {
|
||||
|
||||
async run(initialPrompt: string): Promise<string> {
|
||||
await this.client.start();
|
||||
const session = await this.client.createSession({ model: "gpt-5" });
|
||||
const session = await this.client.createSession({ model: "gpt-5.1-codex-mini" });
|
||||
|
||||
try {
|
||||
while (this.iteration < this.maxIterations) {
|
||||
@@ -115,7 +115,7 @@ class PersistentRalphLoop {
|
||||
async run(initialPrompt: string): Promise<string> {
|
||||
await fs.mkdir(this.workDir, { recursive: true });
|
||||
await this.client.start();
|
||||
const session = await this.client.createSession({ model: "gpt-5" });
|
||||
const session = await this.client.createSession({ model: "gpt-5.1-codex-mini" });
|
||||
|
||||
try {
|
||||
// Store initial prompt
|
||||
|
||||
Reference in New Issue
Block a user