Skip to content

Commit ab82acc

Browse files
committed
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
1 parent bb9f63a commit ab82acc

3 files changed

Lines changed: 114 additions & 83 deletions

File tree

cookbook/copilot-sdk/dotnet/ralph-loop.md

Lines changed: 77 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ RALPH-loop is a development methodology for iterative AI-powered task completion
2020
2121
## Example Scenario
2222
23-
You need to iteratively improve code until all tests pass. Instead of asking Claude to "write perfect code," you use RALPH-loop to:
23+
You need to iteratively improve code until all tests pass. Instead of asking the model to "write perfect code," you use RALPH-loop to:
2424
2525
1. Send the initial prompt with clear success criteria
26-
2. Claude writes code and tests
27-
3. Claude runs tests and sees failures
26+
2. The model writes code and tests
27+
3. The model runs tests and sees failures
2828
4. Loop automatically re-sends the prompt
29-
5. Claude reads test output and previous code, fixes issues
29+
5. The model reads test output and previous code, fixes issues
3030
6. Repeat until all tests pass and completion promise is output
3131
3232
## Basic Implementation
@@ -52,56 +52,66 @@ public class RalphLoop
5252
public async Task<string> RunAsync(string prompt)
5353
{
5454
await _client.StartAsync();
55-
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
5655
5756
try
5857
{
59-
while (_iteration < _maxIterations)
60-
{
61-
_iteration++;
62-
Console.WriteLine($"\n--- Iteration {_iteration} ---");
58+
var session = await _client.CreateSessionAsync(
59+
new SessionConfig { Model = "gpt-5.1-codex-mini" });
6360
61+
try
62+
{
6463
var done = new TaskCompletionSource<string>();
6564
session.On(evt =>
6665
{
6766
if (evt is AssistantMessageEvent msg)
6867
{
6968
_lastResponse = msg.Data.Content;
70-
done.SetResult(msg.Data.Content);
69+
done.TrySetResult(msg.Data.Content);
7170
}
7271
});
7372
74-
// Send prompt (on first iteration) or continuation
75-
var messagePrompt = _iteration == 1
76-
? prompt
77-
: $"{prompt}\n\nPrevious attempt:\n{_lastResponse}\n\nContinue iterating...";
73+
while (_iteration < _maxIterations)
74+
{
75+
_iteration++;
76+
Console.WriteLine($"\n--- Iteration {_iteration} ---");
7877
79-
await session.SendAsync(new MessageOptions { Prompt = messagePrompt });
80-
var response = await done.Task;
78+
done = new TaskCompletionSource<string>();
8179
82-
// Check for completion promise
83-
if (response.Contains(_completionPromise))
84-
{
85-
Console.WriteLine($"✓ Completion promise detected: {_completionPromise}");
86-
return response;
80+
// Send prompt (on first iteration) or continuation
81+
var messagePrompt = _iteration == 1
82+
? prompt
83+
: $"{prompt}\n\nPrevious attempt:\n{_lastResponse}\n\nContinue iterating...";
84+
85+
await session.SendAsync(new MessageOptions { Prompt = messagePrompt });
86+
var response = await done.Task;
87+
88+
// Check for completion promise
89+
if (response.Contains(_completionPromise))
90+
{
91+
Console.WriteLine($"✓ Completion promise detected: {_completionPromise}");
92+
return response;
93+
}
94+
95+
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
8796
}
8897
89-
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
98+
throw new InvalidOperationException(
99+
$"Max iterations ({_maxIterations}) reached without completion promise");
100+
}
101+
finally
102+
{
103+
await session.DisposeAsync();
90104
}
91-
92-
throw new InvalidOperationException(
93-
$"Max iterations ({_maxIterations}) reached without completion promise");
94105
}
95106
finally
96107
{
97-
await session.DisposeAsync();
98108
await _client.StopAsync();
99109
}
100110
}
101111
}
102112
103113
// Usage
104-
var loop = new RalphLoop(maxIterations: 5, completionPromise: "DONE");
114+
var loop = new RalphLoop(maxIterations: 5, completionPromise: "COMPLETE");
105115
var result = await loop.RunAsync("Your task here");
106116
Console.WriteLine(result);
107117
```
@@ -115,38 +125,31 @@ public class PersistentRalphLoop
115125
{
116126
private readonly string _workDir;
117127
private readonly CopilotClient _client;
128+
private readonly int _maxIterations;
118129
private int _iteration = 0;
119130
120131
public PersistentRalphLoop(string workDir, int maxIterations = 10)
121132
{
122133
_workDir = workDir;
134+
_maxIterations = maxIterations;
123135
Directory.CreateDirectory(_workDir);
124136
_client = new CopilotClient();
125137
}
126138
127139
public async Task<string> RunAsync(string prompt)
128140
{
129141
await _client.StartAsync();
130-
var session = await _client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" });
131142
132143
try
133144
{
134-
// Store initial prompt
135-
var promptFile = Path.Combine(_workDir, "prompt.md");
136-
await File.WriteAllTextAsync(promptFile, prompt);
145+
var session = await _client.CreateSessionAsync(
146+
new SessionConfig { Model = "gpt-5.1-codex-mini" });
137147
138-
while (_iteration < 10)
148+
try
139149
{
140-
_iteration++;
141-
Console.WriteLine($"\n--- Iteration {_iteration} ---");
142-
143-
// Build context including previous work
144-
var contextBuilder = new StringBuilder(prompt);
145-
var previousOutput = Path.Combine(_workDir, $"output-{_iteration - 1}.txt");
146-
if (File.Exists(previousOutput))
147-
{
148-
contextBuilder.AppendLine($"\nPrevious iteration output:\n{await File.ReadAllTextAsync(previousOutput)}");
149-
}
150+
// Store initial prompt
151+
var promptFile = Path.Combine(_workDir, "prompt.md");
152+
await File.WriteAllTextAsync(promptFile, prompt);
150153
151154
var done = new TaskCompletionSource<string>();
152155
string response = "";
@@ -155,29 +158,48 @@ public class PersistentRalphLoop
155158
if (evt is AssistantMessageEvent msg)
156159
{
157160
response = msg.Data.Content;
158-
done.SetResult(msg.Data.Content);
161+
done.TrySetResult(msg.Data.Content);
159162
}
160163
});
161164
162-
await session.SendAsync(new MessageOptions { Prompt = contextBuilder.ToString() });
163-
await done.Task;
165+
while (_iteration < _maxIterations)
166+
{
167+
_iteration++;
168+
Console.WriteLine($"\n--- Iteration {_iteration} ---");
164169
165-
// Persist output
166-
await File.WriteAllTextAsync(
167-
Path.Combine(_workDir, $"output-{_iteration}.txt"),
168-
response);
170+
done = new TaskCompletionSource<string>();
169171
170-
if (response.Contains("COMPLETE"))
171-
{
172-
return response;
172+
// Build context including previous work
173+
var contextBuilder = new StringBuilder(prompt);
174+
var previousOutput = Path.Combine(_workDir, $"output-{_iteration - 1}.txt");
175+
if (File.Exists(previousOutput))
176+
{
177+
contextBuilder.AppendLine($"\nPrevious iteration output:\n{await File.ReadAllTextAsync(previousOutput)}");
178+
}
179+
180+
await session.SendAsync(new MessageOptions { Prompt = contextBuilder.ToString() });
181+
await done.Task;
182+
183+
// Persist output
184+
await File.WriteAllTextAsync(
185+
Path.Combine(_workDir, $"output-{_iteration}.txt"),
186+
response);
187+
188+
if (response.Contains("COMPLETE"))
189+
{
190+
return response;
191+
}
173192
}
174-
}
175193
176-
throw new InvalidOperationException("Max iterations reached");
194+
throw new InvalidOperationException("Max iterations reached");
195+
}
196+
finally
197+
{
198+
await session.DisposeAsync();
199+
}
177200
}
178201
finally
179202
{
180-
await session.DisposeAsync();
181203
await _client.StopAsync();
182204
}
183205
}

cookbook/copilot-sdk/dotnet/recipe/ralph-loop.cs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -62,54 +62,63 @@ public RalphLoop(int maxIterations = 10, string completionPromise = "COMPLETE")
6262
public async Task<string> RunAsync(string initialPrompt)
6363
{
6464
await _client.StartAsync();
65-
var session = await _client.CreateSessionAsync(new SessionConfig
66-
{
67-
Model = "gpt-5.1-codex-mini"
68-
});
6965

7066
try
7167
{
72-
while (_iteration < _maxIterations)
73-
{
74-
_iteration++;
75-
Console.WriteLine($"\n=== Iteration {_iteration}/{_maxIterations} ===");
68+
var session = await _client.CreateSessionAsync(new SessionConfig
69+
{
70+
Model = "gpt-5.1-codex-mini"
71+
});
7672

73+
try
74+
{
7775
var done = new TaskCompletionSource<string>();
7876
session.On(evt =>
7977
{
8078
if (evt is AssistantMessageEvent msg)
8179
{
8280
_lastResponse = msg.Data.Content;
83-
done.SetResult(msg.Data.Content);
81+
done.TrySetResult(msg.Data.Content);
8482
}
8583
});
8684

87-
var currentPrompt = BuildIterationPrompt(initialPrompt);
88-
Console.WriteLine($"Sending prompt (length: {currentPrompt.Length})...");
85+
while (_iteration < _maxIterations)
86+
{
87+
_iteration++;
88+
Console.WriteLine($"\n=== Iteration {_iteration}/{_maxIterations} ===");
89+
90+
done = new TaskCompletionSource<string>();
8991

90-
await session.SendAsync(new MessageOptions { Prompt = currentPrompt });
91-
var response = await done.Task;
92+
var currentPrompt = BuildIterationPrompt(initialPrompt);
93+
Console.WriteLine($"Sending prompt (length: {currentPrompt.Length})...");
9294

93-
var summary = response.Length > 200
94-
? response.Substring(0, 200) + "..."
95-
: response;
96-
Console.WriteLine($"Response: {summary}");
95+
await session.SendAsync(new MessageOptions { Prompt = currentPrompt });
96+
var response = await done.Task;
9797

98-
if (response.Contains(_completionPromise))
99-
{
100-
Console.WriteLine($"\n✓ Completion promise detected: '{_completionPromise}'");
101-
return response;
98+
var summary = response.Length > 200
99+
? response.Substring(0, 200) + "..."
100+
: response;
101+
Console.WriteLine($"Response: {summary}");
102+
103+
if (response.Contains(_completionPromise))
104+
{
105+
Console.WriteLine($"\n✓ Completion promise detected: '{_completionPromise}'");
106+
return response;
107+
}
108+
109+
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
102110
}
103111

104-
Console.WriteLine($"Iteration {_iteration} complete. Continuing...");
112+
throw new InvalidOperationException(
113+
$"Max iterations ({_maxIterations}) reached without completion promise: '{_completionPromise}'");
114+
}
115+
finally
116+
{
117+
await session.DisposeAsync();
105118
}
106-
107-
throw new InvalidOperationException(
108-
$"Max iterations ({_maxIterations}) reached without completion promise: '{_completionPromise}'");
109119
}
110120
finally
111121
{
112-
await session.DisposeAsync();
113122
await _client.StopAsync();
114123
}
115124
}

cookbook/copilot-sdk/nodejs/ralph-loop.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class RalphLoop {
4949
5050
async run(initialPrompt: string): Promise<string> {
5151
await this.client.start();
52-
const session = await this.client.createSession({ model: "gpt-5" });
52+
const session = await this.client.createSession({ model: "gpt-5.1-codex-mini" });
5353
5454
try {
5555
while (this.iteration < this.maxIterations) {
@@ -115,7 +115,7 @@ class PersistentRalphLoop {
115115
async run(initialPrompt: string): Promise<string> {
116116
await fs.mkdir(this.workDir, { recursive: true });
117117
await this.client.start();
118-
const session = await this.client.createSession({ model: "gpt-5" });
118+
const session = await this.client.createSession({ model: "gpt-5.1-codex-mini" });
119119
120120
try {
121121
// Store initial prompt

0 commit comments

Comments
 (0)