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
|
## 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
|
1. Send the initial prompt with clear success criteria
|
||||||
2. Claude writes code and tests
|
2. The model writes code and tests
|
||||||
3. Claude runs tests and sees failures
|
3. The model runs tests and sees failures
|
||||||
4. Loop automatically re-sends the prompt
|
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
|
6. Repeat until all tests pass and completion promise is output
|
||||||
|
|
||||||
## Basic Implementation
|
## Basic Implementation
|
||||||
@@ -52,56 +52,66 @@ public class RalphLoop
|
|||||||
public async Task<string> RunAsync(string prompt)
|
public async Task<string> RunAsync(string prompt)
|
||||||
{
|
{
|
||||||
await _client.StartAsync();
|
await _client.StartAsync();
|
||||||
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (_iteration < _maxIterations)
|
var session = await _client.CreateSessionAsync(
|
||||||
{
|
new SessionConfig { Model = "gpt-5.1-codex-mini" });
|
||||||
_iteration++;
|
|
||||||
Console.WriteLine($"\n--- Iteration {_iteration} ---");
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
var done = new TaskCompletionSource<string>();
|
var done = new TaskCompletionSource<string>();
|
||||||
session.On(evt =>
|
session.On(evt =>
|
||||||
{
|
{
|
||||||
if (evt is AssistantMessageEvent msg)
|
if (evt is AssistantMessageEvent msg)
|
||||||
{
|
{
|
||||||
_lastResponse = msg.Data.Content;
|
_lastResponse = msg.Data.Content;
|
||||||
done.SetResult(msg.Data.Content);
|
done.TrySetResult(msg.Data.Content);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send prompt (on first iteration) or continuation
|
while (_iteration < _maxIterations)
|
||||||
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}");
|
_iteration++;
|
||||||
return response;
|
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
|
finally
|
||||||
{
|
{
|
||||||
await session.DisposeAsync();
|
|
||||||
await _client.StopAsync();
|
await _client.StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usage
|
// 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");
|
var result = await loop.RunAsync("Your task here");
|
||||||
Console.WriteLine(result);
|
Console.WriteLine(result);
|
||||||
```
|
```
|
||||||
@@ -115,11 +125,13 @@ public class PersistentRalphLoop
|
|||||||
{
|
{
|
||||||
private readonly string _workDir;
|
private readonly string _workDir;
|
||||||
private readonly CopilotClient _client;
|
private readonly CopilotClient _client;
|
||||||
|
private readonly int _maxIterations;
|
||||||
private int _iteration = 0;
|
private int _iteration = 0;
|
||||||
|
|
||||||
public PersistentRalphLoop(string workDir, int maxIterations = 10)
|
public PersistentRalphLoop(string workDir, int maxIterations = 10)
|
||||||
{
|
{
|
||||||
_workDir = workDir;
|
_workDir = workDir;
|
||||||
|
_maxIterations = maxIterations;
|
||||||
Directory.CreateDirectory(_workDir);
|
Directory.CreateDirectory(_workDir);
|
||||||
_client = new CopilotClient();
|
_client = new CopilotClient();
|
||||||
}
|
}
|
||||||
@@ -127,26 +139,17 @@ public class PersistentRalphLoop
|
|||||||
public async Task<string> RunAsync(string prompt)
|
public async Task<string> RunAsync(string prompt)
|
||||||
{
|
{
|
||||||
await _client.StartAsync();
|
await _client.StartAsync();
|
||||||
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Store initial prompt
|
var session = await _client.CreateSessionAsync(
|
||||||
var promptFile = Path.Combine(_workDir, "prompt.md");
|
new SessionConfig { Model = "gpt-5.1-codex-mini" });
|
||||||
await File.WriteAllTextAsync(promptFile, prompt);
|
|
||||||
|
|
||||||
while (_iteration < 10)
|
try
|
||||||
{
|
{
|
||||||
_iteration++;
|
// Store initial prompt
|
||||||
Console.WriteLine($"\n--- Iteration {_iteration} ---");
|
var promptFile = Path.Combine(_workDir, "prompt.md");
|
||||||
|
await File.WriteAllTextAsync(promptFile, prompt);
|
||||||
// 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)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var done = new TaskCompletionSource<string>();
|
var done = new TaskCompletionSource<string>();
|
||||||
string response = "";
|
string response = "";
|
||||||
@@ -155,29 +158,48 @@ public class PersistentRalphLoop
|
|||||||
if (evt is AssistantMessageEvent msg)
|
if (evt is AssistantMessageEvent msg)
|
||||||
{
|
{
|
||||||
response = msg.Data.Content;
|
response = msg.Data.Content;
|
||||||
done.SetResult(msg.Data.Content);
|
done.TrySetResult(msg.Data.Content);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await session.SendAsync(new MessageOptions { Prompt = contextBuilder.ToString() });
|
while (_iteration < _maxIterations)
|
||||||
await done.Task;
|
|
||||||
|
|
||||||
// Persist output
|
|
||||||
await File.WriteAllTextAsync(
|
|
||||||
Path.Combine(_workDir, $"output-{_iteration}.txt"),
|
|
||||||
response);
|
|
||||||
|
|
||||||
if (response.Contains("COMPLETE"))
|
|
||||||
{
|
{
|
||||||
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
|
finally
|
||||||
{
|
{
|
||||||
await session.DisposeAsync();
|
|
||||||
await _client.StopAsync();
|
await _client.StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,54 +62,63 @@ public class RalphLoop
|
|||||||
public async Task<string> RunAsync(string initialPrompt)
|
public async Task<string> RunAsync(string initialPrompt)
|
||||||
{
|
{
|
||||||
await _client.StartAsync();
|
await _client.StartAsync();
|
||||||
var session = await _client.CreateSessionAsync(new SessionConfig
|
|
||||||
{
|
|
||||||
Model = "gpt-5.1-codex-mini"
|
|
||||||
});
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (_iteration < _maxIterations)
|
var session = await _client.CreateSessionAsync(new SessionConfig
|
||||||
{
|
{
|
||||||
_iteration++;
|
Model = "gpt-5.1-codex-mini"
|
||||||
Console.WriteLine($"\n=== Iteration {_iteration}/{_maxIterations} ===");
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
var done = new TaskCompletionSource<string>();
|
var done = new TaskCompletionSource<string>();
|
||||||
session.On(evt =>
|
session.On(evt =>
|
||||||
{
|
{
|
||||||
if (evt is AssistantMessageEvent msg)
|
if (evt is AssistantMessageEvent msg)
|
||||||
{
|
{
|
||||||
_lastResponse = msg.Data.Content;
|
_lastResponse = msg.Data.Content;
|
||||||
done.SetResult(msg.Data.Content);
|
done.TrySetResult(msg.Data.Content);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var currentPrompt = BuildIterationPrompt(initialPrompt);
|
while (_iteration < _maxIterations)
|
||||||
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}'");
|
_iteration++;
|
||||||
return response;
|
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
|
finally
|
||||||
{
|
{
|
||||||
await session.DisposeAsync();
|
|
||||||
await _client.StopAsync();
|
await _client.StopAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class RalphLoop {
|
|||||||
|
|
||||||
async run(initialPrompt: string): Promise<string> {
|
async run(initialPrompt: string): Promise<string> {
|
||||||
await this.client.start();
|
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 {
|
try {
|
||||||
while (this.iteration < this.maxIterations) {
|
while (this.iteration < this.maxIterations) {
|
||||||
@@ -115,7 +115,7 @@ class PersistentRalphLoop {
|
|||||||
async run(initialPrompt: string): Promise<string> {
|
async run(initialPrompt: string): Promise<string> {
|
||||||
await fs.mkdir(this.workDir, { recursive: true });
|
await fs.mkdir(this.workDir, { recursive: true });
|
||||||
await this.client.start();
|
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 {
|
try {
|
||||||
// Store initial prompt
|
// Store initial prompt
|
||||||
|
|||||||
Reference in New Issue
Block a user