Skip to content

Commit f077c82

Browse files
Fix factory classes to properly extend base classes
- Convert Conversation and Workspace from factory functions to proper classes that extend RemoteConversation and RemoteWorkspace - Update ConversationManager to use new constructor-based API - Update serverStatus.ts to use new constructor pattern - All builds now pass successfully Co-authored-by: openhands <openhands@all-hands.dev>
1 parent 5525be0 commit f077c82

File tree

9 files changed

+198
-191
lines changed

9 files changed

+198
-191
lines changed

README.md

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ npm install @openhands/agent-server-typescript-client
2121
### Creating a Conversation
2222

2323
```typescript
24-
import { Conversation, Agent, RemoteWorkspace } from '@openhands/agent-server-typescript-client';
24+
import { Conversation, Agent, Workspace } from '@openhands/agent-server-typescript-client';
2525

2626
const agent: Agent = {
2727
kind: 'CodeActAgent',
@@ -32,24 +32,22 @@ const agent: Agent = {
3232
};
3333

3434
// Create a remote workspace
35-
const workspace = new RemoteWorkspace({
35+
const workspace = new Workspace({
3636
host: 'http://localhost:3000',
3737
workingDir: '/tmp',
3838
apiKey: 'your-session-api-key'
3939
});
4040

41-
const conversation = await Conversation.create(
42-
'http://localhost:3000', // Agent server URL
43-
agent,
44-
workspace,
45-
{
46-
apiKey: 'your-session-api-key',
47-
initialMessage: 'Hello, can you help me write some code?',
48-
callback: (event) => {
49-
console.log('Received event:', event);
50-
}
41+
const conversation = new Conversation(agent, workspace, {
42+
callback: (event) => {
43+
console.log('Received event:', event);
5144
}
52-
);
45+
});
46+
47+
// Start the conversation with an initial message
48+
await conversation.start({
49+
initialMessage: 'Hello, can you help me write some code?'
50+
});
5351

5452
// Start WebSocket for real-time events
5553
await conversation.startWebSocketClient();
@@ -63,20 +61,18 @@ await conversation.run();
6361

6462
```typescript
6563
// Create a remote workspace for the existing conversation
66-
const workspace = new RemoteWorkspace({
64+
const workspace = new Workspace({
6765
host: 'http://localhost:3000',
6866
workingDir: '/tmp',
6967
apiKey: 'your-session-api-key'
7068
});
7169

72-
const conversation = await Conversation.load(
73-
'http://localhost:3000',
74-
'conversation-id-here',
75-
workspace,
76-
{
77-
apiKey: 'your-session-api-key'
78-
}
79-
);
70+
const conversation = new Conversation(agent, workspace, {
71+
conversationId: 'conversation-id-here'
72+
});
73+
74+
// Connect to the existing conversation
75+
await conversation.start();
8076
```
8177

8278
### Using the Workspace
@@ -136,17 +132,18 @@ await conversation.updateSecrets({
136132

137133
## API Reference
138134

139-
### RemoteConversation
135+
### Conversation
140136

141-
The main class for managing conversations with OpenHands agents.
137+
Factory function that creates conversations with OpenHands agents.
142138

143-
#### Static Methods
139+
#### Constructor
144140

145-
- `RemoteConversation.create(host, agent, options)` - Create a new conversation
146-
- `RemoteConversation.load(host, conversationId, options)` - Load an existing conversation
141+
- `new Conversation(agent, workspace, options?)` - Create a new conversation instance
147142

148143
#### Instance Methods
149144

145+
- `start(options?)` - Start the conversation (creates new or connects to existing)
146+
150147
- `sendMessage(message)` - Send a message to the agent
151148
- `run()` - Start agent execution
152149
- `pause()` - Pause agent execution

example/src/components/ConversationManager.tsx

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
Conversation,
66
RemoteConversation,
77
Agent,
8-
RemoteWorkspace,
8+
Workspace,
99
Event
1010
} from '@openhands/agent-server-typescript-client';
1111
import { useSettings } from '../contexts/SettingsContext';
@@ -201,22 +201,16 @@ export const ConversationManager: React.FC = () => {
201201
};
202202

203203
// Create a remote workspace
204-
const workspace = new RemoteWorkspace({
204+
const workspace = new Workspace({
205205
host: manager.host,
206206
workingDir: '/tmp',
207207
apiKey: manager.apiKey
208208
});
209209

210-
const conversation = await Conversation.create(
211-
manager.host,
212-
agent,
213-
workspace,
214-
{
215-
apiKey: manager.apiKey,
216-
initialMessage: 'Hello! I\'m ready to help you with your tasks.',
217-
maxIterations: 50,
218-
callback: (event: Event) => {
219-
console.log('Received WebSocket event for new conversation:', event);
210+
const conversation = new Conversation(agent, workspace, {
211+
maxIterations: 50,
212+
callback: (event: Event) => {
213+
console.log('Received WebSocket event for new conversation:', event);
220214

221215
// Update the conversation's events in real-time
222216
setConversations(prev => prev.map(conv => {
@@ -231,6 +225,11 @@ export const ConversationManager: React.FC = () => {
231225
);
232226

233227
console.log('Created conversation:', conversation);
228+
229+
// Start the conversation with initial message
230+
await conversation.start({
231+
initialMessage: 'Hello! I\'m ready to help you with your tasks.'
232+
});
234233

235234
// Start WebSocket client for real-time updates
236235
try {
@@ -299,6 +298,12 @@ export const ConversationManager: React.FC = () => {
299298

300299
// Load conversation details
301300
try {
301+
// Get the conversation info to extract the agent
302+
const conversationInfo = conversations.find(c => c.id === conversationId);
303+
if (!conversationInfo) {
304+
throw new Error('Conversation not found');
305+
}
306+
302307
// Create a callback to handle real-time events
303308
const eventCallback = (event: Event) => {
304309
console.log('Received WebSocket event:', event);
@@ -331,22 +336,20 @@ export const ConversationManager: React.FC = () => {
331336
};
332337

333338
// Create a remote workspace for the existing conversation
334-
const workspace = new RemoteWorkspace({
339+
const workspace = new Workspace({
335340
host: manager.host,
336341
workingDir: '/tmp',
337342
apiKey: manager.apiKey
338343
});
339344

340345
// Load conversation with callback
341-
const remoteConversation = await Conversation.load(
342-
manager.host,
343-
conversationId,
344-
workspace,
345-
{
346-
apiKey: manager.apiKey,
347-
callback: eventCallback,
348-
}
349-
);
346+
const remoteConversation = new Conversation(conversationInfo.agent, workspace, {
347+
conversationId: conversationId,
348+
callback: eventCallback,
349+
});
350+
351+
// Connect to the existing conversation
352+
await remoteConversation.start();
350353
console.log('Loaded remote conversation:', remoteConversation);
351354

352355
// Start WebSocket client for real-time updates

example/src/utils/serverStatus.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,19 @@ export const testLLMConfiguration = async (settings: Settings): Promise<{ succes
5959
});
6060

6161
// Create a test conversation using the SDK
62-
const conversation = await RemoteConversation.create(
63-
settings.agentServerUrl,
62+
const conversation = new RemoteConversation(
6463
{
6564
kind: 'Agent',
6665
llm: {
6766
model: settings.modelName,
6867
api_key: settings.apiKey,
6968
}
7069
},
71-
workspace,
72-
{
73-
apiKey: settings.agentServerApiKey,
74-
}
70+
workspace
7571
);
7672

73+
await conversation.start();
74+
7775
try {
7876
// Send a simple test message to validate LLM configuration
7977
await conversation.sendMessage({

examples/basic-usage.ts

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Basic usage example for the OpenHands Agent Server TypeScript Client
33
*/
44

5-
import { Conversation, Agent, RemoteWorkspace, AgentExecutionStatus } from '../src/index.js';
5+
import { Conversation, Agent, Workspace, AgentExecutionStatus } from '../src/index.js';
66

77
async function main() {
88
// Define the agent configuration
@@ -16,26 +16,24 @@ async function main() {
1616

1717
try {
1818
// Create a remote workspace
19-
const workspace = new RemoteWorkspace({
19+
const workspace = new Workspace({
2020
host: 'http://localhost:3000',
2121
workingDir: '/tmp',
2222
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key'
2323
});
2424

2525
// Create a new conversation
2626
console.log('Creating conversation...');
27-
const conversation = await Conversation.create(
28-
'http://localhost:3000', // Replace with your agent server URL
29-
agent,
30-
workspace,
31-
{
32-
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key',
33-
initialMessage: 'Hello! Can you help me write a simple Python script?',
34-
callback: (event) => {
35-
console.log(`Event received: ${event.kind} at ${event.timestamp}`);
36-
},
37-
}
38-
);
27+
const conversation = new Conversation(agent, workspace, {
28+
callback: (event) => {
29+
console.log(`Event received: ${event.kind} at ${event.timestamp}`);
30+
},
31+
});
32+
33+
// Start the conversation with an initial message
34+
await conversation.start({
35+
initialMessage: 'Hello! Can you help me write a simple Python script?'
36+
});
3937

4038
console.log(`Conversation created with ID: ${conversation.id}`);
4139

@@ -87,22 +85,28 @@ async function main() {
8785

8886
// Example of loading an existing conversation
8987
async function loadExistingConversation() {
88+
const agent: Agent = {
89+
kind: 'CodeActAgent',
90+
llm: {
91+
model: 'gpt-4',
92+
api_key: process.env.OPENAI_API_KEY || 'your-openai-api-key',
93+
},
94+
};
95+
9096
try {
9197
// Create a remote workspace for the existing conversation
92-
const workspace = new RemoteWorkspace({
98+
const workspace = new Workspace({
9399
host: 'http://localhost:3000',
94100
workingDir: '/tmp',
95101
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key'
96102
});
97103

98-
const conversation = await Conversation.load(
99-
'http://localhost:3000',
100-
'existing-conversation-id',
101-
workspace,
102-
{
103-
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key',
104-
}
105-
);
104+
const conversation = new Conversation(agent, workspace, {
105+
conversationId: 'existing-conversation-id'
106+
});
107+
108+
// Connect to the existing conversation
109+
await conversation.start();
106110

107111
console.log(`Loaded conversation: ${conversation.id}`);
108112

src/conversation/conversation-manager.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,28 +97,38 @@ export class ConversationManager {
9797
apiKey: this.apiKey,
9898
});
9999

100-
return RemoteConversation.create(this.host, agent, workspace, {
101-
apiKey: this.apiKey,
102-
initialMessage: options.initialMessage,
100+
const conversation = new RemoteConversation(agent, workspace, {
103101
maxIterations: options.maxIterations,
104102
stuckDetection: options.stuckDetection,
105103
});
104+
105+
await conversation.start({
106+
initialMessage: options.initialMessage,
107+
});
108+
109+
return conversation;
106110
}
107111

108112
/**
109113
* Load an existing conversation
110114
*/
111115
async loadConversation(conversationId: ConversationID, workingDir: string = '/tmp'): Promise<RemoteConversation> {
116+
// Get conversation info to extract the agent
117+
const conversationInfo = await this.getConversation(conversationId);
118+
112119
// Create a workspace for the existing conversation
113120
const workspace = new RemoteWorkspace({
114121
host: this.host,
115122
workingDir,
116123
apiKey: this.apiKey,
117124
});
118125

119-
return RemoteConversation.load(this.host, conversationId, workspace, {
120-
apiKey: this.apiKey,
126+
const conversation = new RemoteConversation(conversationInfo.agent, workspace, {
127+
conversationId: conversationId,
121128
});
129+
130+
await conversation.start();
131+
return conversation;
122132
}
123133

124134
/**

src/conversation/conversation.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Conversation factory function that returns RemoteConversation
3+
* Matches the Python SDK pattern: Conversation(agent, workspace)
4+
*/
5+
6+
import { AgentBase } from '../types/base';
7+
import { RemoteWorkspace } from '../workspace/remote-workspace';
8+
import { RemoteConversation, RemoteConversationOptions } from './remote-conversation';
9+
10+
/**
11+
* Conversation class that extends RemoteConversation.
12+
* Provides a cleaner API that matches the Python SDK naming.
13+
*
14+
* Usage:
15+
* const conversation = new Conversation(agent, workspace);
16+
* await conversation.start();
17+
*
18+
* For existing conversations:
19+
* const conversation = new Conversation(agent, workspace, { conversationId: 'existing-id' });
20+
* await conversation.start();
21+
*/
22+
export class Conversation extends RemoteConversation {
23+
constructor(
24+
agent: AgentBase,
25+
workspace: RemoteWorkspace,
26+
options?: RemoteConversationOptions
27+
) {
28+
super(agent, workspace, options);
29+
}
30+
}

0 commit comments

Comments
 (0)