Skip to content

Commit 5525be0

Browse files
feat: Update SDK API to use Agent and Conversation aliases with explicit workspace creation
- Add Agent type alias for AgentBase to provide cleaner user-facing API - Add Conversation const alias for RemoteConversation for consistency with Python SDK - Update create/load methods to require explicit RemoteWorkspace parameter - Modify ConversationManager to create RemoteWorkspace instances explicitly - Update all examples and documentation to use new API pattern: - Use Agent instead of AgentBase - Use Conversation instead of RemoteConversation - Explicitly construct RemoteWorkspace objects - Update example React app and basic usage examples - Maintain backward compatibility by keeping original exports This change aligns the TypeScript SDK more closely with the Python SDK architecture and provides a more intuitive API for end users. Co-authored-by: openhands <openhands@all-hands.dev>
1 parent 6960987 commit 5525be0

File tree

8 files changed

+113
-42
lines changed

8 files changed

+113
-42
lines changed

README.md

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

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

26-
const agent: AgentBase = {
27-
name: 'CodeActAgent',
26+
const agent: Agent = {
27+
kind: 'CodeActAgent',
2828
llm: {
2929
model: 'gpt-4',
3030
api_key: 'your-openai-api-key'
3131
}
3232
};
3333

34-
const conversation = await RemoteConversation.create(
34+
// Create a remote workspace
35+
const workspace = new RemoteWorkspace({
36+
host: 'http://localhost:3000',
37+
workingDir: '/tmp',
38+
apiKey: 'your-session-api-key'
39+
});
40+
41+
const conversation = await Conversation.create(
3542
'http://localhost:3000', // Agent server URL
3643
agent,
44+
workspace,
3745
{
3846
apiKey: 'your-session-api-key',
3947
initialMessage: 'Hello, can you help me write some code?',
@@ -54,9 +62,17 @@ await conversation.run();
5462
### Loading an Existing Conversation
5563

5664
```typescript
57-
const conversation = await RemoteConversation.load(
65+
// Create a remote workspace for the existing conversation
66+
const workspace = new RemoteWorkspace({
67+
host: 'http://localhost:3000',
68+
workingDir: '/tmp',
69+
apiKey: 'your-session-api-key'
70+
});
71+
72+
const conversation = await Conversation.load(
5873
'http://localhost:3000',
5974
'conversation-id-here',
75+
workspace,
6076
{
6177
apiKey: 'your-session-api-key'
6278
}

example/src/components/ConversationManager.tsx

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import React, { useState, useEffect } from 'react';
22
import {
33
ConversationManager as SDKConversationManager,
44
ConversationInfo,
5+
Conversation,
56
RemoteConversation,
6-
AgentBase,
7+
Agent,
8+
RemoteWorkspace,
79
Event
810
} from '@openhands/agent-server-typescript-client';
911
import { useSettings } from '../contexts/SettingsContext';
@@ -118,7 +120,7 @@ export const ConversationManager: React.FC = () => {
118120
useEffect(() => {
119121
return () => {
120122
if (selectedConversation?.remoteConversation) {
121-
selectedConversation.remoteConversation.stopWebSocketClient().catch(err => {
123+
selectedConversation.remoteConversation.stopWebSocketClient().catch((err: any) => {
122124
console.warn('Failed to stop WebSocket client on unmount:', err);
123125
});
124126
}
@@ -190,17 +192,25 @@ export const ConversationManager: React.FC = () => {
190192
setError(null);
191193
try {
192194
// Create a simple agent configuration
193-
const agent: AgentBase = {
195+
const agent: Agent = {
194196
kind: 'Agent',
195197
llm: {
196198
model: settings.modelName,
197199
api_key: settings.apiKey || ''
198200
}
199201
};
200202

201-
const conversation = await RemoteConversation.create(
203+
// Create a remote workspace
204+
const workspace = new RemoteWorkspace({
205+
host: manager.host,
206+
workingDir: '/tmp',
207+
apiKey: manager.apiKey
208+
});
209+
210+
const conversation = await Conversation.create(
202211
manager.host,
203212
agent,
213+
workspace,
204214
{
205215
apiKey: manager.apiKey,
206216
initialMessage: 'Hello! I\'m ready to help you with your tasks.',
@@ -320,10 +330,18 @@ export const ConversationManager: React.FC = () => {
320330
}
321331
};
322332

333+
// Create a remote workspace for the existing conversation
334+
const workspace = new RemoteWorkspace({
335+
host: manager.host,
336+
workingDir: '/tmp',
337+
apiKey: manager.apiKey
338+
});
339+
323340
// Load conversation with callback
324-
const remoteConversation = await RemoteConversation.load(
341+
const remoteConversation = await Conversation.load(
325342
manager.host,
326343
conversationId,
344+
workspace,
327345
{
328346
apiKey: manager.apiKey,
329347
callback: eventCallback,

example/src/utils/serverStatus.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Settings } from '../components/SettingsModal';
2-
import { HttpClient, RemoteConversation } from '@openhands/agent-server-typescript-client';
2+
import { HttpClient, RemoteConversation, RemoteWorkspace } from '@openhands/agent-server-typescript-client';
33

44
export interface ServerStatus {
55
isConnected: boolean;
@@ -51,6 +51,13 @@ export const testLLMConfiguration = async (settings: Settings): Promise<{ succes
5151
return { success: false, error: `Server not reachable: ${healthCheck.error}` };
5252
}
5353

54+
// Create a workspace for the test conversation
55+
const workspace = new RemoteWorkspace({
56+
host: settings.agentServerUrl,
57+
workingDir: '/tmp/test-workspace',
58+
apiKey: settings.agentServerApiKey,
59+
});
60+
5461
// Create a test conversation using the SDK
5562
const conversation = await RemoteConversation.create(
5663
settings.agentServerUrl,
@@ -61,13 +68,9 @@ export const testLLMConfiguration = async (settings: Settings): Promise<{ succes
6168
api_key: settings.apiKey,
6269
}
6370
},
71+
workspace,
6472
{
6573
apiKey: settings.agentServerApiKey,
66-
workspace: {
67-
type: 'local',
68-
path: '/tmp/test-workspace',
69-
working_dir: '/tmp/test-workspace'
70-
}
7174
}
7275
);
7376

examples/basic-usage.ts

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

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

77
async function main() {
88
// Define the agent configuration
9-
const agent: AgentBase = {
10-
name: 'CodeActAgent',
9+
const agent: Agent = {
10+
kind: 'CodeActAgent',
1111
llm: {
1212
model: 'gpt-4',
1313
api_key: process.env.OPENAI_API_KEY || 'your-openai-api-key',
1414
},
1515
};
1616

1717
try {
18+
// Create a remote workspace
19+
const workspace = new RemoteWorkspace({
20+
host: 'http://localhost:3000',
21+
workingDir: '/tmp',
22+
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key'
23+
});
24+
1825
// Create a new conversation
1926
console.log('Creating conversation...');
20-
const conversation = await RemoteConversation.create(
27+
const conversation = await Conversation.create(
2128
'http://localhost:3000', // Replace with your agent server URL
2229
agent,
30+
workspace,
2331
{
2432
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key',
2533
initialMessage: 'Hello! Can you help me write a simple Python script?',
@@ -80,9 +88,17 @@ async function main() {
8088
// Example of loading an existing conversation
8189
async function loadExistingConversation() {
8290
try {
83-
const conversation = await RemoteConversation.load(
91+
// Create a remote workspace for the existing conversation
92+
const workspace = new RemoteWorkspace({
93+
host: 'http://localhost:3000',
94+
workingDir: '/tmp',
95+
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key'
96+
});
97+
98+
const conversation = await Conversation.load(
8499
'http://localhost:3000',
85100
'existing-conversation-id',
101+
workspace,
86102
{
87103
apiKey: process.env.SESSION_API_KEY || 'your-session-api-key',
88104
}

src/conversation/conversation-manager.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import { HttpClient } from '../client/http-client';
66
import { RemoteConversation } from './remote-conversation';
7+
import { RemoteWorkspace } from '../workspace/remote-workspace';
78
import {
89
ConversationInfo,
910
ConversationSearchRequest,
@@ -86,20 +87,36 @@ export class ConversationManager {
8687
initialMessage?: string;
8788
maxIterations?: number;
8889
stuckDetection?: boolean;
89-
workspace?: any;
90+
workingDir?: string;
9091
} = {}
9192
): Promise<RemoteConversation> {
92-
return RemoteConversation.create(this.host, agent, {
93+
// Create a workspace for the conversation
94+
const workspace = new RemoteWorkspace({
95+
host: this.host,
96+
workingDir: options.workingDir || '/tmp',
9397
apiKey: this.apiKey,
94-
...options,
98+
});
99+
100+
return RemoteConversation.create(this.host, agent, workspace, {
101+
apiKey: this.apiKey,
102+
initialMessage: options.initialMessage,
103+
maxIterations: options.maxIterations,
104+
stuckDetection: options.stuckDetection,
95105
});
96106
}
97107

98108
/**
99109
* Load an existing conversation
100110
*/
101-
async loadConversation(conversationId: ConversationID): Promise<RemoteConversation> {
102-
return RemoteConversation.load(this.host, conversationId, {
111+
async loadConversation(conversationId: ConversationID, workingDir: string = '/tmp'): Promise<RemoteConversation> {
112+
// Create a workspace for the existing conversation
113+
const workspace = new RemoteWorkspace({
114+
host: this.host,
115+
workingDir,
116+
apiKey: this.apiKey,
117+
});
118+
119+
return RemoteConversation.load(this.host, conversationId, workspace, {
103120
apiKey: this.apiKey,
104121
});
105122
}

src/conversation/remote-conversation.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -189,12 +189,12 @@ export class RemoteConversation {
189189
static async create(
190190
host: string,
191191
agent: AgentBase,
192+
workspace: RemoteWorkspace,
192193
options: {
193194
apiKey?: string;
194195
initialMessage?: string;
195196
maxIterations?: number;
196197
stuckDetection?: boolean;
197-
workspace?: any;
198198
callback?: ConversationCallbackType;
199199
} = {}
200200
): Promise<RemoteConversation> {
@@ -220,7 +220,7 @@ export class RemoteConversation {
220220
initial_message: initialMessage,
221221
max_iterations: options.maxIterations || 50,
222222
stuck_detection: options.stuckDetection ?? true,
223-
workspace: options.workspace || { type: 'local', working_dir: '/tmp' },
223+
workspace: { type: 'local', working_dir: workspace.workingDir },
224224
};
225225

226226
console.log('Full request object:', JSON.stringify(request, null, 2));
@@ -235,19 +235,16 @@ export class RemoteConversation {
235235
callback: options.callback,
236236
});
237237

238-
// Initialize workspace
239-
conversation._workspace = new RemoteWorkspace({
240-
host,
241-
workingDir: conversationInfo.workspace?.working_dir || '/tmp',
242-
apiKey: options.apiKey,
243-
});
238+
// Use the provided workspace
239+
conversation._workspace = workspace;
244240

245241
return conversation;
246242
}
247243

248244
static async load(
249245
host: string,
250246
conversationId: string,
247+
workspace: RemoteWorkspace,
251248
options: {
252249
apiKey?: string;
253250
callback?: ConversationCallbackType;
@@ -266,12 +263,8 @@ export class RemoteConversation {
266263
);
267264
const conversationInfo = response.data;
268265

269-
// Initialize workspace
270-
conversation._workspace = new RemoteWorkspace({
271-
host,
272-
workingDir: conversationInfo.workspace?.working_dir || '/tmp',
273-
apiKey: options.apiKey,
274-
});
266+
// Use the provided workspace
267+
conversation._workspace = workspace;
275268

276269
return conversation;
277270
}
@@ -284,3 +277,6 @@ export class RemoteConversation {
284277
}
285278
}
286279
}
280+
281+
// Alias for user-facing API
282+
export const Conversation = RemoteConversation;

src/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
// Main conversation and workspace classes
9-
export { RemoteConversation } from './conversation/remote-conversation';
9+
export { RemoteConversation, Conversation } from './conversation/remote-conversation';
1010
export { ConversationManager } from './conversation/conversation-manager';
1111
export { RemoteWorkspace } from './workspace/remote-workspace';
1212
export { RemoteState } from './conversation/remote-state';
@@ -33,6 +33,7 @@ export type {
3333
TextContent,
3434
ImageContent,
3535
AgentBase,
36+
Agent,
3637
LLM,
3738
ServerInfo,
3839
Success,
@@ -75,7 +76,7 @@ export type { RemoteConversationOptions } from './conversation/remote-conversati
7576
export type { ConversationManagerOptions } from './conversation/conversation-manager';
7677

7778
// Re-import for default export
78-
import { RemoteConversation } from './conversation/remote-conversation';
79+
import { RemoteConversation, Conversation } from './conversation/remote-conversation';
7980
import { ConversationManager } from './conversation/conversation-manager';
8081
import { RemoteWorkspace } from './workspace/remote-workspace';
8182
import { RemoteState } from './conversation/remote-state';
@@ -87,6 +88,7 @@ import { EventSortOrder, AgentExecutionStatus } from './types/base';
8788
// Default export for convenience
8889
export default {
8990
RemoteConversation,
91+
Conversation,
9092
ConversationManager,
9193
RemoteWorkspace,
9294
RemoteState,

src/types/base.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ export interface AgentBase {
7979
[key: string]: any;
8080
}
8181

82+
// Alias for user-facing API
83+
export type Agent = AgentBase;
84+
8285
export interface LLM {
8386
model: string;
8487
api_key?: string;

0 commit comments

Comments
 (0)